﻿using UnityEngine;
using UnityEditor;
using System.IO;
using Newtonsoft.Json;
using System.Collections.Generic;


class AssetBundleInfo
{
    public Bundle bundle;
    public HashSet<string> assetPaths = new HashSet<string>();
//    public HashSet<string> dependenceAssetPaths = new HashSet<string>();
//    public HashSet<string> realDependenceAssetPaths = new HashSet<string>();
}

class CommitBundleInfo
{
    public string key;
    public bool delete;
}

public class SortByExtComparer : System.Collections.IComparer 
{ 
    public int Compare(object a, object b)
    {
        string aExt = System.IO.Path.GetExtension((string)a);
        string bExt = System.IO.Path.GetExtension((string)b);
        return string.Compare(aExt, bExt);
    } 
}


public class SortByDependenceCountComparer : System.Collections.IComparer 
{
    public Dictionary<string, int> dcm;
    public int Compare(object a, object b)
    {
        return dcm[(string)b] - dcm[(string)a];
    }
}


public class AssetBundleTool
{
    public delegate HashSet<string> GetSceneAllPrefabPaths(string assetPath);
    public static GetSceneAllPrefabPaths getSceneAllPrefabPaths;

    public const string resPath = "Assets/asset_bundle";
    static int defaultBundleSize = 50; //mb
    static int prefabCountPerbundle = 1000;
    static bool splitDepthGroup = false;
    static bool useBundleNameToFindCommonAsset = true;

//    [MenuItem("Custom/AssetBundleTool/Build")]
//    static void Build()
//    {
//        string assetBundleDirectory = "asset_bundle";
//        if(!Directory.Exists(assetBundleDirectory))
//        {
//            Directory.CreateDirectory(assetBundleDirectory);
//        }
//
//        BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.ChunkBasedCompression | BuildAssetBundleOptions.DisableWriteTypeTree, EditorUserBuildSettings.activeBuildTarget);
//    }
    
    [MenuItem("Custom/AssetBundleTool/BuildByConfig")]
    public static void BuildByConfig()
    {
        string assetBundleDir = "/Users/auto_build/jenkins/workspace/mmorpg_android_alpha/asset_bundle";

        string outputDir = assetBundleDir + "/manifest";
        string oldVersionConfigDir = assetBundleDir + "/old_version_config";
        string commitConfigPath = assetBundleDir + "/commit_config.txt";
        int minAppVersion = 0;
        BuildByConfig2(outputDir, oldVersionConfigDir, commitConfigPath, minAppVersion);
    }

    public static void BuildByConfigByCmd()
    {
        // string assetBundleDir = "asset_bundle";
        // assetBundleDir += "/" + EditorUserBuildSettings.activeBuildTarget.getPlatform() + "/" + VersionEx.compile_type;

        string outputDir = null;
        string oldVersionConfigDir = null;
        string commitConfigPath = null;
        int minAppVersion = 0;
        string[] args = System.Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; i++)
        {
            if (args[i] == "-output_path")
            {
                outputDir = args[++i];
            }
            else if (args[i] == "-old_config_path")
            {
                oldVersionConfigDir = args[++i];
            }
            else if (args[i] == "-commit_config_path")
            {
                commitConfigPath = args[++i];
            }
            else if (args[i] == "-min_app_version")
            {
                minAppVersion = int.Parse(args[++i]);
            }
        }

        BuildByConfig2(outputDir, oldVersionConfigDir, commitConfigPath, minAppVersion);
    }

    static void SearchLoopByManifest(string bundleName, List<string> stack, HashSet<string> searchedNodeSet, Dictionary<string, HashSet<string>> dependenceMap, HashSet<string[]> loopSet)
    {
        if (string.IsNullOrEmpty(bundleName))
        {
            return;
        }

        int index = stack.IndexOf(bundleName);
        if(index < 0)
        {
            if (!searchedNodeSet.Contains(bundleName))
            {
                searchedNodeSet.Add(bundleName);
                stack.Add(bundleName);
                HashSet<string> dependencies = null;
                dependenceMap.TryGetValue(bundleName, out dependencies);

                if (dependencies == null)
                {
                    throw new System.Exception("dependencies is null: " + bundleName);
                }

                foreach (var d in dependencies)
                {
                    SearchLoopByManifest(d, stack, searchedNodeSet, dependenceMap, loopSet);
                }

                stack.Remove(bundleName);
            }
        }
        else
        {
            string[] loop = new string[stack.Count - index];
            for (int i = index; i < stack.Count; i++)
            {
                loop[i - index] = stack[i];
            }
            loopSet.Add(loop);
        }
    }

    [MenuItem("Assets/Custom/AssetBundleTool/CheckLoopByManifest")]
    static void CheckLoopByManifestBySelection()
    {
        EditorCommonTool.ProcessSelectionObjs("*", (path) =>
            {
                AssetBundle a = AssetBundle.LoadFromFile(path);
                AssetBundleManifest manifest = a.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                CheckLoopByManifest(manifest);
            });
    }

    static void CheckLoopByManifest(AssetBundleManifest manifest)
    {
        Dictionary<string, HashSet<string>> dependenceMap = new Dictionary<string, HashSet<string>>();
        string[] abs = manifest.GetAllAssetBundles();
        for(int i = 0; i < abs.Length; i++)
        {
            string abName = abs[i];
            string[] directDependencies = manifest.GetDirectDependencies(abName);
            dependenceMap.Add(abName, new HashSet<string>(directDependencies));
        }

        LinkedList<string> q = new LinkedList<string>();
        foreach (var entry in dependenceMap)
        {
            q.AddLast(entry.Key);
        }

        HashSet<string> searchedNodeSet = new HashSet<string>();
        HashSet<string[]> loopSet = new HashSet<string[]>();
        while (q.Count > 0)
        {
            string bundleName = q.First.Value;
            q.RemoveFirst();

            List<string> stack = new List<string>();

            SearchLoopByManifest(bundleName, stack, searchedNodeSet, dependenceMap, loopSet);

            foreach (var node in searchedNodeSet)
            {
                q.Remove(node);
            }
        }

        int maxPrintLoopNum = 100;
        if (loopSet.Count > 0)
        {
            int i = 0;
            string log = "bundle loops:";
            foreach (string[] bundles in loopSet)
            {
                if (i >= maxPrintLoopNum)
                {
                    break;
                }

                log += i + ":";
                for (int j = 0; j < bundles.Length + 1; j++)
                {
                    string bundleName = bundles[j % bundles.Length];
                    log += bundleName;
                    if (j != bundles.Length)
                    {
                        log += " -> ";
                    }
                }

                log += "\n";
                i++;
            }

            throw new System.Exception(log);
        }
    }

    public static bool MonoScriptInfoEquals(Dictionary<string, List<KeyValuePair<string, string>>> monoScriptInfoMap0, Dictionary<string, List<KeyValuePair<string, string>>> monoScriptInfoMap1)
    {
        if (monoScriptInfoMap0 == monoScriptInfoMap1)
        {
            return true;
        }

        if (monoScriptInfoMap0.Count != monoScriptInfoMap1.Count)
        {
            return false;
        }

        Dictionary<string, List<KeyValuePair<string, string>>>.Enumerator e = monoScriptInfoMap0.GetEnumerator();
        while (e.MoveNext())
        {
            List<KeyValuePair<string, string>> fields1 = null;
            if (!monoScriptInfoMap1.TryGetValue(e.Current.Key, out fields1))
            {
                return false;
            }

            List<KeyValuePair<string, string>> fields0 = e.Current.Value;

            if (fields0 == null && fields1 == null)
            {
                continue;
            }

            if (fields0 == null || fields1 == null)
            {
                return false;
            }

            if (fields0.Count != fields1.Count)
            {
                return false;
            }

            for (int i = 0; i < fields0.Count; i++)
            {
                KeyValuePair<string, string> field1 = fields1[i];
                int index = fields0.FindIndex((a) =>
                    {
                        return a.Key == field1.Key && a.Value == field1.Value;
                    });
                if(index < 0)
                {
                    return false;
                }
            }
        }

        return true;
    }


    public static bool ScriptInfoEqualsOrInclude(Dictionary<string, List<KeyValuePair<string, string>>> monoScriptInfoMap0, Dictionary<string, List<KeyValuePair<string, string>>> monoScriptInfoMap1, bool isLua)
    {
        if (monoScriptInfoMap0 == monoScriptInfoMap1)
        {
            return true;
        }

        if (monoScriptInfoMap0.Count < monoScriptInfoMap1.Count)
        {
            return false;
        }

        Dictionary<string, List<KeyValuePair<string, string>>>.Enumerator e = monoScriptInfoMap0.GetEnumerator();
        while (e.MoveNext())
        {
            List<KeyValuePair<string, string>> fields1 = null;
//            if (!monoScriptInfoMap1.TryGetValue(e.Current.Key, out fields1))
//            {
//                return false;
//            }

            if (!monoScriptInfoMap1.TryGetValue(e.Current.Key, out fields1))
            {
                continue;
            }

            List<KeyValuePair<string, string>> fields0 = e.Current.Value;

            if (fields0 == null && fields1 == null)
            {
                continue;
            }

            if (fields0 == null || fields1 == null)
            {
                return false;
            }

            if (isLua)
            {
                if (fields0.Count < fields1.Count)
                {
                    return false;
                }
            }
            else
            {
                if (fields0.Count != fields1.Count)
                {
                    return false;
                }
            }

            for (int i = 0; i < fields1.Count; i++)
            {
                KeyValuePair<string, string> field1 = fields1[i];
                int index = fields0.FindIndex((a) =>
                    {
                        return a.Key == field1.Key && a.Value == field1.Value;
                    });
                if(index < 0)
                {
                    return false;
                }
            }
        }

        return true;
    }

    public static void BuildByConfig2(string outputDir, string oldVersionConfigDir, string outputCommitPath, int minAppVersion)
    {
        string filePath = resPath + "/" + BundleManager.configName + "_editor.json";
        string str = CommonUtil.fileToString(filePath);
        BundleConfig config = JsonConvert.DeserializeObject<BundleConfig>(str);

        List<AssetBundleBuild> buildInfos = new List<AssetBundleBuild>();
//        for (int i = 0; i < config.bundles.Length; i++)
//        {
//            BundleLayer bundleLayer = config.bundles[i];
//            foreach (var entry in bundleLayer)
//            {
//                AssetBundleBuild buildInfo = new AssetBundleBuild();
//                buildInfo.assetBundleName = entry.Key;
//                buildInfo.assetNames = GetBundleAssetPaths(entry.Value, null).ToArray();
//                buildInfos.Add(buildInfo);
//            }
//        }

        Debug.Log("BuildByConfig2 1");

        Dictionary<string, string> nameMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, string>>(resPath + "/" + "name_map.txt");
        BundleLayer bundleLayer = config.bundles;
        foreach (var entry in bundleLayer)
        {
            string newBundleName = nameMap[entry.Key];
            AssetBundleBuild buildInfo = new AssetBundleBuild();
            buildInfo.assetBundleName = newBundleName;
            buildInfo.assetNames = GetBundleAssetPaths(entry.Value, null).ToArray();
            buildInfos.Add(buildInfo);
        }

        Debug.Log("BuildByConfig2 2");

        BundleVersionConfig lastVersionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(outputDir + "/" + BundleVersionManager.configName);
//        BundleVersionConfig lastVersionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(outputDir + "/" + Path.GetFileName(resPath) + "/" + BundleVersionManager.configName);

        Dictionary<string, uint> bundleSizeMap = new Dictionary<string, uint>();
        // if (Directory.Exists(oldVersionConfigDir))
        // {
        //     string[] allConfigPaths = Directory.GetFiles(oldVersionConfigDir, "config_*", SearchOption.AllDirectories);
        //     for (int i = 0; i < allConfigPaths.Length; i++)
        //     {
        //         BundleVersionConfig tempVersionConfig = JsonConvert.DeserializeObject<BundleVersionConfig>(CommonUtil.fileToString(allConfigPaths[i]));
        //         Dictionary<string, BundleInfo>.Enumerator enumerator = tempVersionConfig.bundles.GetEnumerator();
        //         while (enumerator.MoveNext())
        //         {
        //             string hashName = enumerator.Current.Value.GetHashName();
        //             if (!bundleSizeMap.ContainsKey(hashName))
        //             {
        //                 bundleSizeMap.Add(hashName, enumerator.Current.Value.size);
        //             }
        //         }
        //     }
        // }
            
        if(!Directory.Exists(outputDir))
        {
            Directory.CreateDirectory(outputDir);
        }

        string manifestFileName = Path.GetFileName(outputDir);
//        AssetBundle oldManifestBundle = AssetBundle.LoadFromFile(outputDir + "/" + manifestFileName);
//        string[] oldBundleNames = null;
//        if(oldManifestBundle != null)
//        {
//            AssetBundleManifest oldManifest = oldManifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//            oldBundleNames = oldManifest.GetAllAssetBundles();
//            oldManifestBundle.Unload(true);
//            oldManifestBundle = null;
//        }


        Debug.Log("BuildByConfig2 3: " + buildInfos.Count);

        HashSet<string> modifiedBundles = CommonUtil.ObjectFromJsonFile<HashSet<string>>(resPath + "/" + "modified_bundle.txt");

//        BuildAssetBundleOptions options = BuildAssetBundleOptions.ChunkBasedCompression | BuildAssetBundleOptions.DisableWriteTypeTree;
        BuildAssetBundleOptions options = BuildAssetBundleOptions.ChunkBasedCompression | BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.StrictMode;
//        BuildAssetBundleOptions options = BuildAssetBundleOptions.ChunkBasedCompression | BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.StrictMode;
        {
            AssetBundleManifest newManifest = BuildPipeline.BuildAssetBundles(outputDir, buildInfos.ToArray(), options | BuildAssetBundleOptions.DryRunBuild, EditorUserBuildSettings.activeBuildTarget); 

//            Dictionary<string, string[]> dm = new Dictionary<string, string[]>();
//            string[] bundleNames = newManifest.GetAllAssetBundles();
//            for (int i = 0; i < bundleNames.Length; i++)
//            {
//                dm.Add(bundleNames[i], newManifest.GetDirectDependencies(bundleNames[i]));
//            }
//            CommonUtil.ObjectToJsonFile(dm, "test_manifest.txt");

            CheckLoopByManifest(newManifest);
        }

        Debug.Log("BuildByConfig2 4");

        ConsumeTime.Log("BuildByConfig2 5", ()=>
            {
                if (!RealBuildAssetBundle(buildInfos, manifestFileName, outputDir, oldVersionConfigDir, outputCommitPath, minAppVersion, options, bundleSizeMap, lastVersionConfig, nameMap, true, modifiedBundles))
                {
                    throw new System.Exception("build asset bundle failed");

        //            Debug.LogWarning("force rebuild asset bundle");
        //            RealBuildAssetBundle(buildInfos, manifestFileName, outputDir, oldVersionConfigDir, outputCommitPath, minAppVersion, options, bundleSizeMap, lastVersionConfig, nameMap, true, modifiedBundles);
                }
            });


//        {
//            AssetBundleManifest newManifest = BuildPipeline.BuildAssetBundles(outputDir, buildInfos.ToArray(), options, EditorUserBuildSettings.activeBuildTarget);
//            BundleVersionConfig versionConfig = new BundleVersionConfig();
//            {
//                uint totalSize = 0;
//
//                string[] allBundleNames = newManifest.GetAllAssetBundles();
//                List<string> allBundleNameList = new List<string>(allBundleNames);
//                allBundleNameList.Insert(0, manifestFileName);
//
//                for (int i = 0; i < allBundleNameList.Count; i++)
//                {
//                    string bundleName = allBundleNameList[i];
//                    if (string.IsNullOrEmpty(bundleName))
//                    {
//                        continue;
//                    }
//
//                    string bundleFilePath = outputDir + "/" + bundleName;
//
//                    BundleInfo bundleInfo = new BundleInfo();
//                    bundleInfo.name = bundleName;
//                    if (File.Exists(bundleFilePath))
//                    {
//                        bundleInfo.hash = MD5Hash.GetMD5HashFromFile(bundleFilePath);
//                    }
//                    else
//                    {
//                        BundleInfo oldBundleInfo = null;
//                        if (lastVersionConfig.bundles.TryGetValue(bundleName, out oldBundleInfo))
//                        {
//                            bundleInfo.hash = oldBundleInfo.hash;
//                        }
//                        else
//                        {
//                            throw new System.Exception("bundle hash not found: " + bundleName);
//                        }
//                    }
//
//                    uint fileSize = 0;
//                    if (File.Exists(bundleFilePath))
//                    {
//                        fileSize = (uint)CommonUtil.GetFileSize(bundleFilePath);
//                    }
//                    else
//                    {
//                        if (!bundleSizeMap.TryGetValue(bundleInfo.GetHashName(), out fileSize))
//                        {
//                            throw new System.Exception("old bundle size not found: " + bundleInfo.GetHashName());
//                        }
//                    }
//
//                    bundleInfo.size = fileSize;
//                    bundleInfo.origin_size = bundleInfo.size;
//                    bundleInfo.compress = CompressType.NONE;
//
//                    versionConfig.bundles.Add(bundleName, bundleInfo);
//                    totalSize += fileSize;
//                }
//
//
//                bool needForceBuild = false;
//                BundleVersionConfig oldVersionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(outputDir + "/" + BundleVersionManager.configName);
//                foreach (var modifiedBundleName in modifiedBundles)
//                {
//                    string newName = nameMap[modifiedBundleName];
//                    if(oldVersionConfig.bundles.ContainsKey(newName))
//                    {
//                        if (versionConfig.bundles[newName].hash == oldVersionConfig.bundles[newName].hash)
//                        {
//                            needForceBuild = true;
//                            break;
//                        }
//                    }
//                }
//
//                if (needForceBuild)
//                {
//                    return false;
//                }
//
//
//                List<string> newBundleNames = new List<string>(newManifest.GetAllAssetBundles());
//                newBundleNames.Add(manifestFileName);
//
//                string[] filePaths = Directory.GetFiles(outputDir, "*", SearchOption.AllDirectories);
//                for (int i = 0; i < filePaths.Length; i++)
//                {
//                    string tempPath = filePaths[i];
//                    string ext = Path.GetExtension(tempPath);
//                    if (ext == ".manifest")
//                    {
//                        tempPath = tempPath.Replace(ext, "");
//                        ext = "";
//                    }
//                    else if (!string.IsNullOrEmpty(ext))
//                    {
//                        continue;
//                    }
//
//                    string oldBundleName = Path.GetFileName(tempPath);
//
//                    if (!newBundleNames.Exists((b) =>
//                        {
//                            return b == oldBundleName;
//                        }))
//                    {
//                        CommonUtil.Delete(outputDir + "/" + oldBundleName);
//                        CommonUtil.Delete(outputDir + "/" + oldBundleName + ".manifest");
//                    }
//                }
//
//                versionConfig.version = Version.version_number;
//                versionConfig.bundles = new Dictionary<string, BundleInfo>();
//
//                versionConfig.platform = EditorUserBuildSettings.activeBuildTarget.getPlatform().ToString();
//                versionConfig.compile_type = Version.compile_type.ToString();
//                versionConfig.min_app_version = minAppVersion;
//
//
//                if (minAppVersion <= 0)
//                {
//                    Dictionary<string, List<KeyValuePair<string, string>>> lastMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(outputDir + "/asset_bundle/" + "monoscript_info.txt");
//                    Dictionary<string, List<KeyValuePair<string, string>>> curMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(resPath + "/" + "monoscript_info.txt");
//                    if (lastMonoScriptInfoMap != null && MonoScriptInfoEquals(curMonoScriptInfoMap, lastMonoScriptInfoMap))
//                    {
//                        
//                        versionConfig.min_app_version = lastVersionConfig.min_app_version;
//                    }
//                    else
//                    {
//                        versionConfig.min_app_version = Version.version_number;
//                    }
//                }
//
//
//
//                versionConfig.count = versionConfig.bundles.Count;
//                versionConfig.size = totalSize;
//                versionConfig.origin_size = totalSize;
//                versionConfig.compress = CompressType.NONE;
//                versionConfig.md5 = "";
//
//                string configStr = JsonConvert.SerializeObject(versionConfig);
//                versionConfig.md5 = MD5Hash.GetMD5Hash(configStr);
//                versionConfig.setTime();
//
//                CommonUtil.Copy(outputDir + "/" + BundleVersionManager.configName, outputDir + "/last_" + BundleVersionManager.configName);
//                CommonUtil.ObjectToJsonFile(versionConfig, outputDir + "/" + BundleVersionManager.configName);
//                CommonUtil.Copy(outputDir + "/" + BundleVersionManager.configName, oldVersionConfigDir + "/" + BundleManager.configName + "_" + versionConfig.version + "_" + versionConfig.md5);
//
//                List<string> keys = new List<string>(versionConfig.bundles.Keys);
//                keys.Sort((a, b) =>
//                    {
//                        int sizeA = (int)versionConfig.bundles[a].size;
//                        int sizeB = (int)versionConfig.bundles[b].size;
//                        return sizeB - sizeA;
//                    });
//
//                //                Dictionary<string, string> reverseNameMap = new Dictionary<string, string>();
//                //                foreach (var entry in nameMap)
//                //                {
//                //                    reverseNameMap.Add(entry.Value, entry.Key);
//                //                }
//
//                string log = "";
//                for(int i = 0; i < keys.Count; i++)
//                {
//                    string key = keys[i];
//                    log += "\n" + key + ": " + CommonUtil.toSizeString(versionConfig.bundles[key].size);
//                }
//
//                Debug.Log("bundle size: " + log);
//            }
//
//
//
//            //create commit config file
//            {
//                Dictionary<string, CommitBundleInfo> commitBundleInfoMap = new Dictionary<string, CommitBundleInfo>();
//
//                Dictionary<string, BundleInfo>.Enumerator e = versionConfig.bundles.GetEnumerator();
//                while (e.MoveNext())
//                {
//                    string bundleName = e.Current.Key;
//
////                string bundleFilePath = outputDir + "/" + bundleName;
////                if(File.Exists(bundleFilePath) || localBundleSet.Contains(bundleName))
//                    {
//                        CommitBundleInfo info = new CommitBundleInfo();
//                        info.key = e.Current.Value.GetHashName();
////                        info.delete = !localBundleSet.ContainsKey(bundleName);
//                        info.delete = false;
//                        commitBundleInfoMap.Add(bundleName, info);
//                    }
//                }
//
//                string commitConfigStr = JsonConvert.SerializeObject(commitBundleInfoMap, Formatting.Indented);
//                CommonUtil.stringToFile(outputCommitPath, commitConfigStr);
//            }
//
//            CommonUtil.CopyDirectory(resPath, outputDir + "/" + Path.GetFileName(resPath));
//        }
    }

    class MyProjectVersionComparer: IComparer<int>
    {
        public int Compare(int x, int y)
        {
            return y - x;
        }
    }

    static bool RealBuildAssetBundle(List<AssetBundleBuild> buildInfos, string manifestFileName, string outputDir, string oldVersionConfigDir, string outputCommitPath, int minAppVersion, BuildAssetBundleOptions options, Dictionary<string, uint> bundleSizeMap, BundleVersionConfig lastVersionConfig, Dictionary<string, string> nameMap, bool force, HashSet<string> modifiedBundles)
    {
        if (force)
        {
            options |= BuildAssetBundleOptions.ForceRebuildAssetBundle;
        }

        Debug.Log("build asset bundle with options: " + options);
        AssetBundleManifest newManifest = BuildPipeline.BuildAssetBundles(outputDir, buildInfos.ToArray(), options, EditorUserBuildSettings.activeBuildTarget);
        if (newManifest == null)
        {
            return false;
        }

        BundleVersionConfig versionConfig = new BundleVersionConfig();
        versionConfig.bundle_name_encoded = EditorSwitchConfig.encodeBundleName;
        versionConfig.bundles = new Dictionary<string, BundleInfo>();

        {
            uint totalSize = 0;

            string[] allBundleNames = newManifest.GetAllAssetBundles();
            List<string> allBundleNameList = new List<string>(allBundleNames);
            allBundleNameList.Insert(0, manifestFileName);

            for (int i = 0; i < allBundleNameList.Count; i++)
            {
                string bundleName = allBundleNameList[i];
                if (string.IsNullOrEmpty(bundleName))
                {
                    continue;
                }

                string bundleFilePath = outputDir + "/" + bundleName;

                BundleInfo bundleInfo = new BundleInfo();
                bundleInfo.name = bundleName;
                if (File.Exists(bundleFilePath))
                {
                    bundleInfo.hash = MD5Hash.GetMD5HashFromFile(bundleFilePath);
                }
                else
                {
                    BundleInfo oldBundleInfo = null;
                    if (lastVersionConfig.bundles.TryGetValue(bundleName, out oldBundleInfo))
                    {
                        bundleInfo.hash = oldBundleInfo.hash;
                    }
                    else
                    {
                        throw new System.Exception("bundle hash not found: " + bundleName);
                    }
                }

                uint fileSize = 0;
                if (File.Exists(bundleFilePath))
                {
                    fileSize = (uint)CommonUtil.GetFileSize(bundleFilePath);
                }
                else
                {
                    if (!bundleSizeMap.TryGetValue(bundleInfo.GetHashName(), out fileSize))
                    {
                        throw new System.Exception("old bundle size not found: " + bundleInfo.GetHashName());
                    }
                }

                bundleInfo.size = fileSize;
                bundleInfo.origin_size = bundleInfo.size;
                bundleInfo.compress = CompressType.NONE;

                versionConfig.bundles.Add(bundleName, bundleInfo);
                totalSize += fileSize;
            }


//            if (modifiedBundles != null && !force)
//            {
//                foreach (var modifiedBundleName in modifiedBundles)
//                {
//                    string newName = nameMap[modifiedBundleName];
//                    if (lastVersionConfig.bundles.ContainsKey(newName))
//                    {
//                        if (versionConfig.bundles[newName].hash == lastVersionConfig.bundles[newName].hash)
//                        {
//                            Debug.Log("need force build beacause: " + "bundleName(" + modifiedBundleName + ")" + versionConfig.bundles[newName].hash + " == " + lastVersionConfig.bundles[newName].hash);
//
//                            return false;
//                        }
//                    }
//                }
//            }
                
            List<string> newBundleNames = new List<string>(newManifest.GetAllAssetBundles());
            newBundleNames.Add(manifestFileName);

            string[] filePaths = Directory.GetFiles(outputDir, "*", SearchOption.AllDirectories);
            for (int i = 0; i < filePaths.Length; i++)
            {
                string tempPath = filePaths[i];
                string ext = Path.GetExtension(tempPath);
                if (ext == ".manifest")
                {
                    tempPath = tempPath.Replace(ext, "");
                    ext = "";
                }
                else if (!string.IsNullOrEmpty(ext))
                {
                    continue;
                }

                string oldBundleName = Path.GetFileName(tempPath);

                if (!newBundleNames.Exists((b) =>
                    {
                        return b == oldBundleName;
                    }))
                {
                    CommonUtil.Delete(outputDir + "/" + oldBundleName);
                    CommonUtil.Delete(outputDir + "/" + oldBundleName + ".manifest");
                }
            }

            versionConfig.version = VersionEx.version_number;


            UnityEngine.Debug.LogError("test platfrom: " + EditorUserBuildSettings.activeBuildTarget);
            versionConfig.platform = EditorUserBuildSettings.activeBuildTarget.getPlatform().ToString();
            versionConfig.compile_type = VersionEx.compile_type.ToString();
            versionConfig.min_app_version = minAppVersion;

			
			CommonUtil.CreateDir(oldVersionConfigDir);
			
            if (minAppVersion <= 0)
            {
                versionConfig.min_app_version = VersionEx.version_number;

                string[] oldProjectMonoScriptInfoPaths = System.IO.Directory.GetFiles(oldVersionConfigDir, "project_monoscript_info_*", SearchOption.TopDirectoryOnly);

                SortedDictionary<int, HashSet<string>> projectMonoScriptInfoMap = new SortedDictionary<int, HashSet<string>>(new MyProjectVersionComparer());
                for(int i = 0; i < oldProjectMonoScriptInfoPaths.Length; i++)
                {
                    string p = oldProjectMonoScriptInfoPaths[i];

                    string[] ss = p.Split('_');
                    int version = int.Parse(ss[ss.Length - 2]);

                    if(version < VersionEx.version_number)
                    {
                        HashSet<string> pathSet = null;
                        if(!projectMonoScriptInfoMap.TryGetValue(version, out pathSet))
                        {
                            pathSet = new HashSet<string>();
                            projectMonoScriptInfoMap.Add(version, pathSet);
                        }

                        pathSet.Add(p);
                    }
                }

                Dictionary<string, List<KeyValuePair<string, string>>> curMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(resPath + "/" + "monoscript_info.txt");
                foreach (var item in projectMonoScriptInfoMap)
                {
                    bool canUse = true;
                    foreach(var s in item.Value)
                    {
                        Dictionary<string, List<KeyValuePair<string, string>>> lastMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(s);
                        if(!ScriptInfoEqualsOrInclude(lastMonoScriptInfoMap, curMonoScriptInfoMap, false))
                        {
                            canUse = false;
                            break;
                        }
                    }

                    if(canUse)
                    {
                        versionConfig.min_app_version = item.Key;
                    }
                    else
                    {
                        break;
                    }
                }

//                 {
//                     Dictionary<string, List<KeyValuePair<string, string>>> lastMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(outputDir + "/asset_bundle/" + "monoscript_info.txt");
//                     Dictionary<string, List<KeyValuePair<string, string>>> curMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(resPath + "/" + "monoscript_info.txt");

//                     // Dictionary<string, List<KeyValuePair<string, string>>> lastLuaMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(outputDir + "/asset_bundle/" + "luascript_info.txt");
//                     // Dictionary<string, List<KeyValuePair<string, string>>> curLuaMonoScriptInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, List<KeyValuePair<string, string>>>>(resPath + "/" + "luascript_info.txt");
//                     if (lastMonoScriptInfoMap != null && ScriptInfoEqualsOrInclude(lastMonoScriptInfoMap, curMonoScriptInfoMap, false))
//                     {
// //                        if (lastLuaMonoScriptInfoMap != null && ScriptInfoEqualsOrInclude(lastLuaMonoScriptInfoMap, curLuaMonoScriptInfoMap, true))
//                         {
//                             versionConfig.min_app_version = lastVersionConfig.min_app_version;
//                         }
//                     }
//                 }
            }
                
            versionConfig.count = versionConfig.bundles.Count;
            versionConfig.size = totalSize;
            versionConfig.origin_size = totalSize;
            versionConfig.compress = CompressType.NONE;
            versionConfig.md5 = "";

            string configStr = JsonConvert.SerializeObject(versionConfig);
            versionConfig.md5 = MD5Hash.GetMD5Hash(configStr);
            versionConfig.setTime();

            CommonUtil.Copy(outputDir + "/" + BundleVersionManager.configName, outputDir + "/last_" + BundleVersionManager.configName);
            CommonUtil.ObjectToJsonFile(versionConfig, outputDir + "/" + BundleVersionManager.configName);
            CommonUtil.Copy(outputDir + "/" + BundleVersionManager.configName, oldVersionConfigDir + "/" + BundleManager.configName + "_" + versionConfig.version + "_" + versionConfig.md5);
            CommonUtil.Copy(resPath + "/" + "project_monoscript_info.txt", oldVersionConfigDir + "/" + "project_monoscript_info" + "_" + versionConfig.version + "_" + versionConfig.md5);


            if (modifiedBundles != null)
            {
                string log = "";
                foreach (var modifiedBundleName in modifiedBundles)
                {
                    log += "\n" + modifiedBundleName;
                }

                Debug.Log("modified bundle: " + log);
            }

            HashSet<string> diffSet = new HashSet<string>();
            uint diffSize = 0;
            foreach(var entry in versionConfig.bundles)
            {
                bool same = false;
                if (lastVersionConfig != null)
                {
                    BundleInfo info = null;
                    if (lastVersionConfig.bundles.TryGetValue(entry.Key, out info))
                    {
                        if (info.hash == entry.Value.hash)
                        {
                            same = true;
                        }
                    }
                }

                if (!same)
                {
                    diffSize += entry.Value.size;
                    diffSet.Add(entry.Key);
                }
            }

            List<string> keys = new List<string>(versionConfig.bundles.Keys);
            keys.Sort((a, b) =>
                {
                    int sizeA = (int)versionConfig.bundles[a].size;
                    int sizeB = (int)versionConfig.bundles[b].size;
                    if(diffSet.Contains(a) && !diffSet.Contains(b))
                    {
                        return -1;
                    }
                    else if(diffSet.Contains(b) && !diffSet.Contains(a))
                    {
                        return 1;
                    }

                    return sizeB - sizeA;
                });

            Dictionary<string, string> reverseNameMap = new Dictionary<string, string>();
            foreach (var entry in nameMap)
            {
                reverseNameMap.Add(entry.Value, entry.Key);
            }


            {
                string log = "";
                log += "\n" + "diff_size: " + CommonUtil.toSizeString(diffSize) + " / " + CommonUtil.toSizeString(versionConfig.size);
                for (int i = 0; i < keys.Count; i++)
                {
                    string key = keys[i];
                    log += "\n" + key + ": " + CommonUtil.toSizeString(versionConfig.bundles[key].size);
                    if (diffSet.Contains(key))
                    {
                        log += "  *  ";
                        if (reverseNameMap.ContainsKey(key))
                        {
                            log += reverseNameMap[key];
                        }
                    }
                }
                Debug.Log("bundle size: " + log);
            }
        }



        //create commit config file
        {
            Dictionary<string, CommitBundleInfo> commitBundleInfoMap = new Dictionary<string, CommitBundleInfo>();

            Dictionary<string, BundleInfo>.Enumerator e = versionConfig.bundles.GetEnumerator();
            while (e.MoveNext())
            {
                string bundleName = e.Current.Key;

                //                string bundleFilePath = outputDir + "/" + bundleName;
                //                if(File.Exists(bundleFilePath) || localBundleSet.Contains(bundleName))
                {
                    CommitBundleInfo info = new CommitBundleInfo();
                    info.key = e.Current.Value.GetHashName();
                    //                        info.delete = !localBundleSet.ContainsKey(bundleName);
                    info.delete = false;
                    commitBundleInfoMap.Add(bundleName, info);
                }
            }

            string commitConfigStr = JsonConvert.SerializeObject(commitBundleInfoMap, Formatting.Indented);
            CommonUtil.stringToFile(outputCommitPath, commitConfigStr);
        }

        CommonUtil.Delete(outputDir + "/" + Path.GetFileName(resPath));
        CommonUtil.CopyDirectory(resPath,  Path.GetDirectoryName(outputDir) + "/" + Path.GetFileName(resPath));

        return true;
    }

    [MenuItem("Custom/AssetBundleTool/PresetBuildByCmd")]
    static void PresetBuildByCmd()
    {
        int inAppBundleSize = 0;
        string bundlePath = null;
        string[] args = System.Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; i++)
        {
            if (args[i] == "-asset_bundle_path")
            {
                bundlePath = args[++i];
            }
            else if (args[i] == "-in_app_bundle_size")
            {
                inAppBundleSize = int.Parse(args[++i]);
            }
        }
        
        DeleteInAssetBundleResource();


        string configDir = Path.GetDirectoryName(bundlePath) + "/" + Path.GetFileName(resPath);
        CopyLocalBundleToStreamingAssetDir(bundlePath, configDir, inAppBundleSize);
    }

    [MenuItem("Custom/AssetBundleTool/MoveBundleResToResourcesDir")]
    static void MoveBundleResToResourcesDir()
    {
        string dir = ResourcesEx.BundleResDir;
        string destDir = "Assets/Resources";

        if(System.IO.Directory.Exists(destDir))
        {
            string ret = AssetDatabase.CreateFolder("Assets","ResourcesTemp");
            if(string.IsNullOrEmpty(ret))
            {
                throw new System.Exception("CreateFolder failed: ResourcesTemp");
            }

            string destTempDir = "Assets/ResourcesTemp/Resources";
            string  ret2 = AssetDatabase.MoveAsset(destDir, destTempDir);
            if(!string.IsNullOrEmpty(ret2))
            {
                throw new System.Exception(ret2);
            }
        }

        AssetDatabase.MoveAsset(dir, destDir);
    }



    static void CopyLocalBundleToStreamingAssetDir(string bundlePath, string configDir, int inAppBundleSize)
    {
        string filePath = configDir + "/" + BundleManager.configName + "_editor.json";
        BundleConfig config = CommonUtil.ObjectFromJsonFile<BundleConfig>(filePath);

        string bundleConfigPath = bundlePath + "/" + BundleVersionManager.configName;
        BundleVersionConfig versionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(bundleConfigPath);
        if (versionConfig == null)
        {
            throw new System.Exception(bundleConfigPath + " not found");
        }

        Dictionary<string, string> nameMap = null;
        string nameMapPath = configDir + "/" + "name_map.txt";
        nameMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, string>>(nameMapPath);

//        Dictionary<string, string> reverseNameMap = new Dictionary<string, string>();
//        foreach (var entry in nameMap)
//        {
//            reverseNameMap.Add(entry.Value, entry.Key);
//        }

        Dictionary<string, LocalBundleInfo> localBundleSet = new Dictionary<string, LocalBundleInfo>();
        //set local bundle
        {
            string manifestFileName = Path.GetFileName(bundlePath);
            {
                LocalBundleInfo localBundleInfo = new LocalBundleInfo();
                localBundleInfo.local_type = LocalType.essential;
                localBundleInfo.name = manifestFileName;
                localBundleInfo.hash = versionConfig.bundles[manifestFileName].hash;
                localBundleSet.Add(manifestFileName, localBundleInfo);
            }
            AssetBundle manifestBundle = AssetBundle.LoadFromFile(bundlePath + "/" + manifestFileName);
            AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

//            for (int i = 0; i < config.bundles.Length; i++)
//            {
//                BundleLayer bundleLayer = config.bundles[i];
//                foreach (var entry in bundleLayer)
//                {
//                    if (entry.Value.local_type != LocalType.none)
//                    {
//                        if (!localBundleSet.ContainsKey(entry.Key))
//                        {
//                            {
//                                LocalBundleInfo localBundleInfo = new LocalBundleInfo();
//                                localBundleInfo.local_type = entry.Value.local_type;
//                                localBundleInfo.hash = versionConfig.bundles[entry.Key].hash;
//                                localBundleSet.Add(entry.Key, localBundleInfo);
//                            }
//
//                            string[] ds = manifest.GetAllDependencies(entry.Key);
//                            for (int j = 0; j < ds.Length; j++)
//                            {
//                                if (!localBundleSet.ContainsKey(ds[j]))
//                                {
//                                    LocalBundleInfo localBundleInfo = new LocalBundleInfo();
//                                    localBundleInfo.local_type = config.bundles[i][entry.Key].local_type;
//                                    localBundleInfo.hash = versionConfig.bundles[ds[j]].hash;
//                                    localBundleSet.Add(ds[j], localBundleInfo);
//                                }
//                            }
//                        }
//                    }
//                }
//            }


            BundleLayer bundleLayer = config.bundles;
            foreach (var entry in bundleLayer)
            {
                if (entry.Value.local_type != LocalType.none)
                {
                    string newName = nameMap[entry.Key];
                    if (!localBundleSet.ContainsKey(newName))
                    {
                        {
                            LocalBundleInfo localBundleInfo = new LocalBundleInfo();
                            localBundleInfo.local_type = entry.Value.local_type;
                            localBundleInfo.name = newName;
                            localBundleInfo.hash = versionConfig.bundles[newName].hash;
                            localBundleSet.Add(newName, localBundleInfo);
                        }

                        string[] ds = manifest.GetAllDependencies(newName);
                        for (int j = 0; j < ds.Length; j++)
                        {
                            if (!localBundleSet.ContainsKey(ds[j]))
                            {
                                LocalBundleInfo localBundleInfo = new LocalBundleInfo();
                                localBundleInfo.local_type = entry.Value.local_type;
                                localBundleInfo.name = ds[j];
                                localBundleInfo.hash = versionConfig.bundles[ds[j]].hash;
                                localBundleSet.Add(ds[j], localBundleInfo);
                            }
                        }
                    }
                }
            }

            if (inAppBundleSize != 0)
            {
                uint inAppBundleSizeSum = 0;
                foreach (var entry in localBundleSet)
                {
                    inAppBundleSizeSum += versionConfig.bundles[entry.Key].size;
                }

                List<string> sortedBundleNames = new List<string>();
                sortedBundleNames.AddRange(versionConfig.bundles.Keys);
                sortedBundleNames.Sort((a, b) =>
                    {
                        return (int)versionConfig.bundles[b].size - (int)versionConfig.bundles[a].size;
                    });

                for (int i = 0; i < sortedBundleNames.Count; i++)
                {
                    string bundleName = sortedBundleNames[i];
                    if (!localBundleSet.ContainsKey(bundleName))
                    {
                        BundleInfo info = versionConfig.bundles[bundleName];
                        if (inAppBundleSize < 0 || inAppBundleSizeSum + info.size <= ((uint)inAppBundleSize * 1024 * 1024))
                        {
                            inAppBundleSizeSum += info.size;

                            LocalBundleInfo localBundleInfo = new LocalBundleInfo();
                            localBundleInfo.local_type = LocalType.unessential;
                            localBundleInfo.name = bundleName;
                            localBundleInfo.hash = versionConfig.bundles[bundleName].hash;
                            localBundleSet.Add(bundleName, localBundleInfo);
                        }
                    }
                }
            }
        }


//        Dictionary<string, LocalBundleInfo> oldLocalBundleSet = CommonUtil.ObjectFromJsonFile<Dictionary<string, LocalBundleInfo>>(Application.streamingAssetsPath + "/" + BundleVersionManager.allLocalBundleConfigName + ".bytes");
        Dictionary<string, LocalBundleInfo> oldLocalBundleSet = CommonUtil.ObjectFromJsonFile<Dictionary<string, LocalBundleInfo>>(Application.streamingAssetsPath + "/" + BundleVersionManager.allLocalBundleConfigName);
        if (oldLocalBundleSet != null)
        {
//            BundleVersionConfig oldVersionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(Application.streamingAssetsPath + "/" + BundleVersionManager.configName + ".bytes");
            BundleVersionConfig oldVersionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(Application.streamingAssetsPath + "/" + BundleVersionManager.configName);
            foreach (var entry in oldVersionConfig.bundles)
            {
                if (oldLocalBundleSet.ContainsKey(entry.Key))
                {
                    bool delete = false;
                    if (localBundleSet.ContainsKey(entry.Key))
                    {
                        if (versionConfig.bundles[entry.Key].hash != entry.Value.hash)
                        {
                            delete = true;
                        }
                    }
                    else
                    {
                        delete = true;
                    }

                    if (delete)
                    {
                        string tempPath = Application.streamingAssetsPath + "/" + entry.Value.GetHashName();
                        CommonUtil.Delete(tempPath);
                    }
                }
            }
        }

//        if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
//        {
//            CommonUtil.Copy(bundleConfigPath, ResourcesEx.ResDir + "/" + BundleVersionManager.configName + ".bytes");
//            CommonUtil.ObjectToJsonFile(localBundleSet, ResourcesEx.ResDir + "/" + BundleVersionManager.allLocalBundleConfigName + ".bytes");
//        }
//        else
//        {
            CommonUtil.Copy(bundleConfigPath, Application.streamingAssetsPath + "/" + BundleVersionManager.configName);
            CommonUtil.ObjectToJsonFile(localBundleSet, Application.streamingAssetsPath + "/" + BundleVersionManager.allLocalBundleConfigName);
//        }

        foreach (var entry in versionConfig.bundles)
        {
            if (localBundleSet.ContainsKey(entry.Key))
            {
                string tempPath = Application.streamingAssetsPath + "/" + entry.Value.GetHashName();
                if(!CommonUtil.Exists(tempPath))
                {
                    CommonUtil.Copy(bundlePath + "/" + entry.Key, tempPath);
                }
            }
        }
    }

    [MenuItem("Custom/AssetBundleTool/DeleteInAssetBundleResource")]
    public static void DeleteInAssetBundleResource()
    {
        HashSet<string> excludedAssets = new HashSet<string>();
        string rawFilePath = resPath + "/" + "raw_" + BundleManager.configName + ".json";
        string rawStr = CommonUtil.fileToString(rawFilePath);
        string platformStr = EditorUserBuildSettings.activeBuildTarget.getPlatform().ToString();
        string ConfigStr = rawStr.Replace("${platform}", platformStr);
        BundleConfig rawConfig = JsonConvert.DeserializeObject<BundleConfig>(ConfigStr);

//        {
//            for (int i = 0; i < rawConfig.bundles.Length; i++)
//            {
//                BundleLayer bundleLayer = rawConfig.bundles[i];
//                foreach (var entry in bundleLayer)
//                {
//                    if (entry.Value.excluded_assets != null)
//                    {
//                        for (int j = 0; j < entry.Value.excluded_assets.Length; j++)
//                        {
//                            string assetPath = entry.Value.excluded_assets[j];
//                            excludedAssets.Add(assetPath);
//                            string[] dependencedAssets = AssetDatabase.GetDependencies(assetPath, true);
//                            excludedAssets.Add(dependencedAssets);
//                        }
//                    }
//                }
//            }
//        }
//
        BundleConfig config = rawConfig;
//
//        for (int i = 0; i < config.bundles.Length; i++)
//        {
//            BundleLayer bundleLayer = config.bundles[i];
//            foreach (var entry in bundleLayer)
//            {
//                Bundle bundle = entry.Value;
//                if (bundle.is_dependenced)
//                {
//                    continue;
//                }
//                    
//                if (bundle.dirs == null || bundle.dirs.Length <= 0)
//                {
//                    HashSet<string> assetFiles = GetBundleAssetPaths(bundle, null);
//                    foreach (string assetPath in assetFiles)
//                    {
//                        if (!excludedAssets.Contains(assetPath) && !assetPath.EndsWith(".cginc") && !assetPath.EndsWith(".cs") && !assetPath.EndsWith(".js"))
//                        {
//                            if (assetPath.StartsWith(ResourcesEx.BundleResDir))
//                            {
//                                Trash.Delete(assetPath);
//                            }
//                        }
//                    }
//                }
//                else
//                {
//                    for (int j = 0; j < bundle.dirs.Length; j++)
//                    {
//                        string dir = bundle.dirs[j];
//                        if (!dir.StartsWith(ResourcesEx.BundleResDir))
//                        {
//                            continue;
//                        }
//
//                        if (File.Exists(dir))
//                        {
//                            CommonUtil.Delete(dir);
//                        }
//                        else if (Directory.Exists(dir))
//                        {
//                            bool found = false;
//                            foreach (var excludeAssetPath in excludedAssets)
//                            {
//                                if (excludeAssetPath.StartsWith(dir))
//                                {
//                                    found = true;
//                                    break;
//                                }
//                            }
//
//                            if (found)
//                            {
//                                HashSet<string> deleteAssetPaths = new HashSet<string>();
//                                HashSet<string> assetPaths = GetDirAssetPaths(new string[]{dir});
//                                foreach (var assetPath in assetPaths)
//                                {
//                                    if (!excludedAssets.Contains(assetPath))
//                                    {
//                                        deleteAssetPaths.Add(assetPath);
//                                    }
//                                }
//
//                                Trash.Delete(deleteAssetPaths);
//                            }
//                            else
//                            {
//                                Trash.Delete(dir);
//                            }
//                        }
//                    }
//                }
//            }
//        }
            

        HashSet<string> scenePaths = new HashSet<string>();
//        for(int j = 0; j < config.bundles.Length; j++)
//        {
//            BundleLayer layer = config.bundles[j];
//            foreach (var entry in layer)
//            {
//                if (entry.Value.type == BundleType.scene)
//                {
//                    if (entry.Value.dirs == null || entry.Value.dirs.Length <= 0)
//                    {
//                        scenePaths.Add(GetBundleAssetPaths(entry.Value, null));
//                    }
//                    else
//                    {
//                        for (int i = 0; i < entry.Value.dirs.Length; i++)
//                        {
//                            scenePaths.Add(entry.Value.dirs[i]);
//                        }
//                    }
//                }
//            }
//        }


        BundleLayer layer = config.bundles;
        foreach (var entry in layer)
        {
            if (entry.Value.type == BundleType.scene)
            {
                if (entry.Value.dirs == null || entry.Value.dirs.Length <= 0)
                {
                    scenePaths.Add(GetBundleAssetPaths(entry.Value, null));
                }
                else
                {
                    for (int i = 0; i < entry.Value.dirs.Length; i++)
                    {
                        scenePaths.Add(entry.Value.dirs[i]);
                    }
                }
            }
        }

        List<EditorBuildSettingsScene> newScenes = new List<EditorBuildSettingsScene>();
        for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
        {
            EditorBuildSettingsScene scene = EditorBuildSettings.scenes[i];
            if (!scenePaths.Contains(scene.path))
            {
                newScenes.Add(scene);
            }
        }

        EditorBuildSettings.scenes = newScenes.ToArray();
    }



    static HashSet<string> GetBundleAssetPaths(Bundle bundle, Dictionary<Bundle, HashSet<string>> pathSet)
    {
        if (pathSet != null)
        {
            HashSet<string> result2 = null;
            if (pathSet.TryGetValue(bundle, out result2))
            {
                return result2;
            }
        }
        
        HashSet<string> result = new HashSet<string>();

        if (bundle.type != BundleType.scene || (bundle.dirs != null && bundle.dirs.Length > 0))
        {
            List<string> dirList = new List<string>();
            for (int j = 0; j < bundle.dirs.Length; j++)
            {
                string dirPath = bundle.dirs[j];
                if (File.Exists(dirPath))
                {
                    result.Add(dirPath);
                }
                else if (Directory.Exists(dirPath))
                {
                    dirList.Add(dirPath);
                }
                else
                {
                    string dir = System.IO.Path.GetDirectoryName(dirPath);
                    string fileName = System.IO.Path.GetFileName(dirPath);
                    string[] paths = CommonUtil.GetFiles(dir, fileName + ".*", SearchOption.TopDirectoryOnly);
                    if(paths != null)
                    {
                        foreach(var a in paths)
                        {
                            dirList.Add(a);
                        }
                    }
                }
            }

            if (dirList.Count > 0)
            {
                HashSet<string> tempResult = EditorCommonTool.FindAssets(dirList.ToArray());
                result.Add(tempResult);
            }
        }
        else
        {
            for (int j = 0; j < EditorBuildSettings.scenes.Length; j++)
            {
                EditorBuildSettingsScene scene = EditorBuildSettings.scenes[j];
                if (System.IO.File.Exists(scene.path))
                {
                    result.Add(scene.path);
                }
            }
        }

        if (bundle.excluded_assets != null)
        {
            for (int k = 0; k < bundle.excluded_assets.Length; k++)
            {
                result.Remove(bundle.excluded_assets[k]);
            }
        }

        if (pathSet != null)
        {
            pathSet.Add(bundle, result);
        }

        return result;
    }

    [MenuItem("Custom/AssetBundleTool/CreatePlatformBundleConfig")]
    public static void CreatePlatfromBundleConfig()
    {
        string outputDir = null;
        string svnUserName = null;
        string svnPassword = null;
        string[] args = System.Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; i++)
        {
            if (args[i] == "-output_path")
            {
                outputDir = args[++i];
            }
            else if (args[i] == "-svn_username")
            {
                svnUserName = args[++i];
            }
            else if (args[i] == "-svn_password")
            {
                svnPassword = args[++i];
            }
        }

        string svnOtherParams = "--non-interactive --trust-server-cert-failures=unknown-ca,cn-mismatch,not-yet-valid,other";
        if (!string.IsNullOrEmpty(svnUserName) && !string.IsNullOrEmpty(svnPassword))
        {
            svnOtherParams += " --username " + svnUserName + " --password " + svnPassword;
        }
        string configOutputPath = Path.GetDirectoryName(outputDir) + "/" + Path.GetFileName(resPath);
        CreatePlatfromBundleConfig2(EditorUserBuildSettings.activeBuildTarget, outputDir, configOutputPath, svnOtherParams);
    }

    static Dictionary<string, string> GetAssetGroupNameMap(Dictionary<string, int> depthMap, Dictionary<string, HashSet<string>> dependenceMap)
    {
        HashSet<string> rootPathSet = new HashSet<string>();
        foreach(var entry in depthMap)
        {
            if(entry.Value == 0)
            {
                rootPathSet.Add(entry.Key);
            }
        }

        Dictionary<string, HashSet<string>> assetGroupSetMap = new Dictionary<string, HashSet<string>>();

        foreach(var n in rootPathSet)
        {
            if(!n.EndsWith(".unity"))
            {
                continue;
            }

            HashSet<string> settedMap = new HashSet<string>();
            Queue<string> q = new Queue<string>();
            q.Enqueue(n);
            while(q.Count > 0)
            {
                string s = q.Dequeue();

                if(settedMap.Contains(s))
                {
                    continue;
                }
                settedMap.Add(s);

                HashSet<string> ss = null;
                if(!assetGroupSetMap.TryGetValue(s, out ss))
                {
                    ss = new HashSet<string>();
                    assetGroupSetMap.Add(s, ss);
                }

                ss.Add(n);

                HashSet<string> tt = null;
                if(dependenceMap.TryGetValue(s, out tt))
                {
                    foreach(var t in tt)
                    {
                        if(!settedMap.Contains(t))
                        {
                            q.Enqueue(t);
                        }
                    }
                }
            }
        }

        Dictionary<string, string> assetGroupNameMap = new Dictionary<string, string>();
        foreach(var entry in assetGroupSetMap)
        {
            List<string> rootAssets = new List<string>(entry.Value);
            rootAssets.Sort();

            string groupName = "";
            for(int i = 0; i < rootAssets.Count; i++)
            {
                groupName += rootAssets[i];
                if(i < rootAssets.Count - 1)
                {
                    groupName += "__";
                }
            }

            groupName = MD5Hash.GetMD5Hash(groupName);

            assetGroupNameMap.Add(entry.Key, groupName);
        }

        return assetGroupNameMap;
    }

    static void CreatePrefabBundleGroup(string bundleName, HashSet<string> assetSet, Dictionary<string, int> assetSizeMap, Dictionary<string, HashSet<string>> autoIncludedAssetMap, Dictionary<string, DependenceInfo> dependenceInfoMap, Dictionary<string, AssetBundleInfo> tempMap, LocalType localType, int maxPrefabCountPerBundle, int maxSizePerBundle, BundleConfig oldEditorBundleConfig, HashSet<string> modifiedPathSet)
    {
        Dictionary<string, HashSet<string>> dependenceMap = null;
        Dictionary<string, int> depthMap = null;
        List<string> assetList = SortByDepth(assetSet, dependenceInfoMap, out depthMap, out dependenceMap);



//        {
//            string depthInfoDirPath = resPath + "/depth/";
//            System.IO.Directory.CreateDirectory(depthInfoDirPath);
//            string depthInfoFilePath = depthInfoDirPath + "depth_info_" + bundleName + ".json";
//            CommonUtil.ObjectToJsonFile(depthMap, depthInfoFilePath);
//        }

        for (int i = 0; i < assetList.Count; i++)
        {
            for (int j = i + 1; j < assetList.Count; j++)
            {
                if (assetList[i] == assetList[j])
                {
                    throw new System.Exception("assetPath repeated: " + assetList[i] + "   " + i + "   " + j);
                }
            }
        }


        Dictionary<int, HashSet<string>> depthAssetSetMap = new Dictionary<int, HashSet<string>>();
        if(splitDepthGroup)
        {
            Dictionary<string, string> assetGroupNameMap = GetAssetGroupNameMap(depthMap, dependenceMap);
            Dictionary<string, int> depthGroupRealDepthMap = new Dictionary<string, int>();
            for (int i = 0; i < assetList.Count; i++)
            {
                string assetPath = assetList[i];
 
                int tempDepth = depthMap[assetPath];
                string groupName = "nogroup";
                if(assetGroupNameMap.ContainsKey(assetPath))
                {
                    groupName = assetGroupNameMap[assetPath];
                }
                string key = tempDepth + "__" + groupName;
                int realDepth = 0;
                if(!depthGroupRealDepthMap.TryGetValue(key, out realDepth))
                {
                    realDepth = depthGroupRealDepthMap.Count;
                    depthGroupRealDepthMap.Add(key, realDepth);
                }

                HashSet<string> assetPathSet = null;
                if (!depthAssetSetMap.TryGetValue(realDepth, out assetPathSet))
                {
                    assetPathSet = new HashSet<string>();
                    depthAssetSetMap.Add(realDepth, assetPathSet);
                }
                assetPathSet.Add(assetPath);
            }
        }
        else
        {
            for (int i = 0; i < assetList.Count; i++)
            {
                string assetPath = assetList[i];
                int tempDepth = depthMap[assetPath];

                HashSet<string> assetPathSet = null;
                if (!depthAssetSetMap.TryGetValue(tempDepth, out assetPathSet))
                {
                    assetPathSet = new HashSet<string>();
                    depthAssetSetMap.Add(tempDepth, assetPathSet);
                }
                assetPathSet.Add(assetPath);
            }
        }

        System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"_([0-9]+)_([0-9]+)");
        HashSet<KeyValuePair<int, int>> noModifiedSubBundleSet = new HashSet<KeyValuePair<int, int>>();

        Dictionary<string, KeyValuePair<int, int>> assetPathBundleMap = new Dictionary<string, KeyValuePair<int, int>>();
        if (oldEditorBundleConfig != null)
        {
            BundleLayer.Enumerator e = oldEditorBundleConfig.bundles.GetEnumerator();
            while (e.MoveNext())
            {
                if (e.Current.Key.StartsWith(bundleName))
                {
                    string subBundleName = e.Current.Key.Substring(bundleName.Length, e.Current.Key.Length - bundleName.Length);
                    System.Text.RegularExpressions.Match match = regex.Match(subBundleName);
                    if (match.Success)
                    {
                        int depth = int.Parse(match.Groups[1].Value);
                        int num = int.Parse(match.Groups[2].Value);

                        noModifiedSubBundleSet.Add(new KeyValuePair<int, int>(depth, num));

                        for (int i = 0; i < e.Current.Value.dirs.Length; i++)
                        {
                            assetPathBundleMap.Add(e.Current.Value.dirs[i], new KeyValuePair<int, int>(depth, num));
                        }
                    }
                }
            }
        }

        foreach (var modifiedAssetPath in modifiedPathSet)
        {
            KeyValuePair<int, int> tempPair = new KeyValuePair<int, int>();
            if (assetPathBundleMap.TryGetValue(modifiedAssetPath, out tempPair))
            {
                noModifiedSubBundleSet.Remove(tempPair);
//                assetPathBundleMap.Remove(modifiedAssetPath);
            }
        }
            
        {
            Dictionary<int, HashSet<string>>.Enumerator e2 = depthAssetSetMap.GetEnumerator();
            while (e2.MoveNext())
            {
                HashSet<string> assetPathSet = e2.Current.Value;
                foreach (var assetPath in assetPathSet)
                {
                    KeyValuePair<int, int> tempPair = new KeyValuePair<int, int>();
                    if (assetPathBundleMap.TryGetValue(assetPath, out tempPair))
                    {
                        if (tempPair.Key != e2.Current.Key)
                        {
                            noModifiedSubBundleSet.Remove(tempPair);
                            assetPathBundleMap.Remove(assetPath);
                        }
                    }
                }
            }
        }

        Dictionary<int, int> depthModifiedNumMap = new Dictionary<int, int>();
        Dictionary<int, int> depthModifiedSizeMap = new Dictionary<int, int>();
        {
            Dictionary<int, HashSet<string>>.Enumerator e2 = depthAssetSetMap.GetEnumerator();
            while (e2.MoveNext())
            {
                int modifyAssetNum = 0;
                int modifyAssetSize = 0;
                HashSet<string> assetPathSet = e2.Current.Value;
                foreach (var assetPath in assetPathSet)
                {
                    bool modified = true;
                    KeyValuePair<int, int> tempPair = new KeyValuePair<int, int>();
                    if (assetPathBundleMap.TryGetValue(assetPath, out tempPair))
                    {
                        modified = !noModifiedSubBundleSet.Contains(tempPair);
                    }

                    if (modified)
                    {
                        modifyAssetNum++;

                        if (!assetSizeMap.ContainsKey(assetPath))
                        {
                            throw new System.Exception("size not found: " + assetPath);
                        }

                        modifyAssetNum += autoIncludedAssetMap[assetPath].Count;
                        modifyAssetSize += assetSizeMap[assetPath];
                    }
                }

                depthModifiedNumMap.Add(e2.Current.Key, modifyAssetNum);
                depthModifiedSizeMap.Add(e2.Current.Key, modifyAssetSize);
            }
        }
            
        {
            string sizeLog = "";
            int totalBundleCount = 0;
            int totalSize = 0;
            sizeLog += "per_bundle_max_size: " + CommonUtil.toSizeString(maxSizePerBundle) + "\n";

            Dictionary<int, HashSet<string>>.Enumerator e2 = depthAssetSetMap.GetEnumerator();
            while (e2.MoveNext())
            {
                Dictionary<int, HashSet<string>> indexAssetPathMap = new Dictionary<int, HashSet<string>>();
                int modifyAssetNum = depthModifiedNumMap[e2.Current.Key];
                int modifiedBundleNum = Mathf.CeilToInt(modifyAssetNum / (float)maxPrefabCountPerBundle);
                int modifyAssetSize = depthModifiedSizeMap[e2.Current.Key];
                modifiedBundleNum = Mathf.Max(modifiedBundleNum, Mathf.CeilToInt(modifyAssetSize / (float)maxSizePerBundle));



                HashSet<int> modifiedBundleIndices = new HashSet<int>();
                foreach (var entry in assetPathBundleMap)
                {
                    if (entry.Value.Key != e2.Current.Key)
                    {
                        continue;
                    }

                    if (!noModifiedSubBundleSet.Contains(entry.Value))
                    {
                        
                    }

                    modifiedBundleIndices.Add(entry.Value.Value);
                }
                
                int index = 0;
                for (int i = 0; modifiedBundleIndices.Count < modifiedBundleNum; i++)
                {
                    while (noModifiedSubBundleSet.Contains(new KeyValuePair<int, int>(e2.Current.Key, index)))
                    {
                        index++;
                    }

                    modifiedBundleIndices.Add(index++);
                }


                    
                HashSet<string> assetPathSet = e2.Current.Value;
                List<string> assetPathList = new List<string>(assetPathSet);
                assetPathList.Sort((a, b) =>
                    {
                        int sizeA = 0;
                        if(!assetSizeMap.TryGetValue(a, out sizeA))
                        {
                            throw new System.Exception("size not found: " + a);
                        }

                        int sizeB = 0;
                        if(!assetSizeMap.TryGetValue(b, out sizeB))
                        {
                            throw new System.Exception("size not found: " + b);
                        }

                        return sizeB - sizeA;
                    });

                Dictionary<int, int> modifiedBundleSizeMap = new Dictionary<int, int>();
                Dictionary<int, int> modifiedBundleAssetCountMap = new Dictionary<int, int>();
                foreach(int s in modifiedBundleIndices)
                {
                    modifiedBundleSizeMap.Add(s, 0);
                    modifiedBundleAssetCountMap.Add(s, 0);
                }

                Debug.LogError("test_bundle_count: " + bundleName + "    depth:" + e2.Current.Key + "  modifiedBundleNum:" + modifiedBundleNum + "  assetCount:" + e2.Current.Value.Count + "  modifyAssetNum:" + modifyAssetNum + "  modifyAssetSize:" + modifyAssetSize + "  maxPrefabCountPerBundle:" + maxPrefabCountPerBundle + "  maxSizePerBundle:" + maxSizePerBundle + "  modifiedBundleIndices.Count:" + modifiedBundleIndices.Count);
                    
                for(int m = 0; m < assetPathList.Count; m++)
                {
                    string assetPath = assetPathList[m];
                    KeyValuePair<int, int> tempPair = new KeyValuePair<int, int>();
                    if (assetPathBundleMap.TryGetValue(assetPath, out tempPair))
                    {
                        HashSet<string> newAssetPathSet = null;
                        if (!indexAssetPathMap.TryGetValue(tempPair.Value, out newAssetPathSet))
                        {
                            newAssetPathSet = new HashSet<string>();
                            indexAssetPathMap.Add(tempPair.Value, newAssetPathSet);
                        }

                        newAssetPathSet.Add(assetPath);

                        if (modifiedBundleSizeMap.ContainsKey(tempPair.Value))
                        {
                            modifiedBundleSizeMap[tempPair.Value] += assetSizeMap[assetPath];
                        }
                        else
                        {
                            modifiedBundleSizeMap.Add(tempPair.Value, assetSizeMap[assetPath]);
                        }
                    }
                    else
                    {
                        int realIndex = -1;
                        int minSize = int.MaxValue;
                        foreach(int s in modifiedBundleIndices)
                        {
                            int size = modifiedBundleSizeMap[s];
                            int assetCount = modifiedBundleAssetCountMap[s];

//                            if(assetCount < maxPrefabCountPerBundle)
                            {
                                if (size < minSize)
                                {
                                    realIndex = s;
                                    minSize = size;
                                }
                                else if (size == minSize)
                                {
                                    int c0 = 0;
                                    if (indexAssetPathMap.ContainsKey(s))
                                    {
                                        c0 = indexAssetPathMap[s].Count;
                                    }

                                    int c1 = 0;
                                    if (indexAssetPathMap.ContainsKey(realIndex))
                                    {
                                        c1 = indexAssetPathMap[realIndex].Count;
                                    }

                                    if (c0 < c1)
                                    {
                                        realIndex = s;
                                        minSize = size;
                                    }
                                }
                            }
                        }

                        if(realIndex < 0)
                        {
                            int t = Random.Range(0, modifiedBundleIndices.Count);
                            int[] a = modifiedBundleIndices.ToArray();
                            realIndex = a[t];
                        }

                        HashSet<string> newAssetPathSet = null;
                        try
                        {
                            if (!indexAssetPathMap.TryGetValue(realIndex, out newAssetPathSet))
                            {
                                newAssetPathSet = new HashSet<string>();
                                indexAssetPathMap.Add(realIndex, newAssetPathSet);
                            }
                        }
                        catch(System.Exception e)
                        {
                            Debug.LogError(e.Message + "\n " + modifiedBundleIndices.Count + "  " + realIndex);
                        }
                            
                        newAssetPathSet.Add(assetPath);

                        if (!modifiedBundleSizeMap.ContainsKey(realIndex))
                        {
                            Debug.LogError("index not exist: " + realIndex);
                        }

                        if (!assetSizeMap.ContainsKey(assetPath))
                        {
                            Debug.LogError("index not exist: " + assetPath);
                        }

                        modifiedBundleSizeMap[realIndex] += assetSizeMap[assetPath];
                        modifiedBundleAssetCountMap[realIndex] += autoIncludedAssetMap[assetPath].Count + 1;
                    }
                }

                Dictionary<int, HashSet<string>>.Enumerator e3 = indexAssetPathMap.GetEnumerator();
                while (e3.MoveNext())
                {
                    List<string> assetDirs = new List<string>(e3.Current.Value);

                    Bundle newBundle = new Bundle();
                    newBundle.type = BundleType.normal;
                    newBundle.dirs = assetDirs.ToArray();
                    newBundle.local_type = localType;

                    string newBundleName = bundleName + "_" + e2.Current.Key + "_" + e3.Current.Key;

                    AssetBundleInfo info = new AssetBundleInfo();
                    info.bundle = newBundle;
                    tempMap.Add(newBundleName, info);

                    sizeLog += newBundleName + ": " + CommonUtil.toSizeString(modifiedBundleSizeMap[e3.Current.Key]) + " | " + assetDirs.Count + " | " + modifiedBundleAssetCountMap[e3.Current.Key] + "\n";

                    totalSize += modifiedBundleSizeMap[e3.Current.Key];

                    totalBundleCount++;
                }
            }

            sizeLog += "total_size: " + CommonUtil.toSizeString(totalSize) + "\n";
            Debug.Log("size_log:" + bundleName + "   " + totalBundleCount + "\n" + sizeLog);
        }
    }

    static Dictionary<string, AssetBundleInfo> SplitBundle(string bundleName, HashSet<string> assetSet, Dictionary<string, int> assetSizeMap, Dictionary<string, HashSet<string>> autoIncludedAssetMap, HashSet<string> localBundleFileSet, Dictionary<string, DependenceInfo> dependenceInfoMap, LocalType localType, int prefabCount, int maxSize, BundleConfig oldEditorBundleConfig, HashSet<string> modifiedPathSet)
    {
        HashSet<string> localSet = new HashSet<string>();
        HashSet<string> noLocalSet = new HashSet<string>();
        foreach (var assetPath in assetSet)
        {
            if (localBundleFileSet.Contains(assetPath))
            {
                localSet.Add(assetPath);
            }
            else
            {
                noLocalSet.Add(assetPath);
            }
        }

        Dictionary<string, AssetBundleInfo> tempMap = new Dictionary<string, AssetBundleInfo>();
        CreatePrefabBundleGroup(bundleName + "_local", localSet, assetSizeMap, autoIncludedAssetMap, dependenceInfoMap, tempMap, LocalType.essential, prefabCount, maxSize, oldEditorBundleConfig, modifiedPathSet);
        CreatePrefabBundleGroup(bundleName, noLocalSet, assetSizeMap, autoIncludedAssetMap, dependenceInfoMap, tempMap, localType, prefabCount, maxSize, oldEditorBundleConfig, modifiedPathSet);

        return tempMap;
    }

    class DependenceInfo
    {
        [JsonProperty]
        public HashSet<string> directDependencies;
        HashSet<string> allDependencies;

        public static HashSet<string> ResetPaths(Dictionary<string, DependenceInfo> map, HashSet<string> assetPathSet)
        {
            HashSet<string> result = new HashSet<string>();

            foreach (var assetPath in assetPathSet)
            {
                if (map.ContainsKey(assetPath))
                {
                    result.Add(assetPath);
                }
            }

            foreach (string a in result)
            {
                map.Remove(a);
            }

            return result;
        }

        public static HashSet<string> GetDependencies(Dictionary<string, DependenceInfo> map, string assetPath, bool recursion)
        {
//            if (assetPath == "Assets/BundleRes/shader/custom-ui-Particle Add.shader")
//            {
//                Debug.LogError("");
//            }

            DependenceInfo tempInfo = null;
            if (!map.TryGetValue(assetPath, out tempInfo))
            {
                if (!CommonUtil.Exists(assetPath))
                {
                    return null;
                }

                tempInfo = new DependenceInfo();
                map.Add(assetPath, tempInfo);
            }



            if (!recursion)
            {
                if (tempInfo.directDependencies == null)
                {
                    string[] ds = AssetDatabase.GetDependencies(assetPath, false);
                    tempInfo.directDependencies = new HashSet<string>(ds);
                    tempInfo.directDependencies.Remove(assetPath);
                }

                return tempInfo.directDependencies;
            }
            else
            {
                if (tempInfo.allDependencies == null)
                {
                    //                    tempInfo.allDependencies = new HashSet<string>();
                    //                    HashSet<string> ds = GetDependencies(map, assetPath, false);
                    //                    tempInfo.allDependencies.Add(ds);
                    //
                    //                    foreach (var d in ds)
                    //                    {
                    //                        HashSet<string> ts = GetDependencies(map, d, true);
                    //                        tempInfo.allDependencies.Add(ts);
                    //                    }

                    tempInfo.allDependencies = new HashSet<string>();
                    HashSet<string> ds = GetDependencies(map, assetPath, false);

                    Queue<string> queue = new Queue<string>(ds);
                    while(queue.Count > 0)
                    {
                        string path = queue.Dequeue();
                        if (tempInfo.allDependencies.Contains(path))
                        {
                            continue;
                        }
                        tempInfo.allDependencies.Add(path);

                        HashSet<string> ds2 = GetDependencies(map, path, false);
                        foreach(var d2 in ds2)
                        {
                            if (!tempInfo.allDependencies.Contains(d2))
                            {
                                queue.Enqueue(d2);
                            }
                        }
                    }

//                    tempInfo.allDependencies = new HashSet<string>();
//                    string[] ds = AssetDatabase.GetDependencies(assetPath, true);
//                    tempInfo.allDependencies.Add(ds);
//                    tempInfo.allDependencies.Remove(assetPath);
                }

                return tempInfo.allDependencies;
            }
        }
    }

//    public static void ResetShaderDefaultTextureProperty()
//    {
//        ResetShaderDefaultTextureProperty(EditorUserBuildSettings.activeBuildTarget);
//    }

    static bool isDirectInBundleAsset(string path, BundleConfig config)
    {
        if (Directory.Exists(path))
        {
            return false;
        }

        bool found = false;
        foreach(var entry in config.bundles)
        {
            Bundle bundle = entry.Value;
            string[] dirs = bundle.dirs;
            if (dirs != null)
            {
                for (int i = 0; i < dirs.Length; i++)
                {
                    string dir = dirs[i];
                    if (path == dir || path.StartsWith(dir))
                    {
                        found = true;
                        break;
                    }
                }

                if (found)
                {
                    break;
                }
            }
        }

        if (found)
        {
            return true;
        }

        for (int j = 0; j < EditorBuildSettings.scenes.Length; j++)
        {
            EditorBuildSettingsScene scene = EditorBuildSettings.scenes[j];
            if (path == scene.path)
            {
                return true;
            }
        }

        return false;
    }

    static void UpdateDependenceInfoByBundleConfig(BundleConfig config, Dictionary<Bundle, HashSet<string>> bundleAssetPathSetMap, HashSet<string> modifiedPathSet, ref Dictionary<string, DependenceInfo> dependenceInfoMap)
    {
//        if (modifiedPathSet.Contains("Assets/art/model/ui_model/vehicle00/Materials/petmoto_02_mat1.mat"))
//        {
//            Debug.LogError("test_update dependence: ");
//        }

        if (dependenceInfoMap == null)
        {
            dependenceInfoMap = new Dictionary<string, DependenceInfo>();
            foreach(var entry in config.bundles)
            {
                Bundle bundle = entry.Value;
                HashSet<string> pathSet = GetBundleAssetPaths(bundle, bundleAssetPathSetMap);
                UpdateDependenceInfoByPaths(pathSet, dependenceInfoMap);
            }
        }
        else
        {
            HashSet<string> realModifiedAssetPaths = new HashSet<string>();
//            HashSet<string> deletedPaths = DependenceInfo.ResetPaths(dependenceInfoMap, modifiedPathSet);

            HashSet<string> newModifiedPathSet = new HashSet<string>(modifiedPathSet);
            foreach (var path in modifiedPathSet)
            {
                if (string.IsNullOrEmpty(System.IO.Path.GetExtension(path)))
                {
                    foreach (var entry in dependenceInfoMap)
                    {
                        if (entry.Key.StartsWith(path) && entry.Key.Length > path.Length && (entry.Key[path.Length] == '/' || path[path.Length - 1] == '/'))
                        {
                            newModifiedPathSet.Add(entry.Key);
                        }
                    }
                }
            }
            modifiedPathSet = newModifiedPathSet;

            foreach (var path in modifiedPathSet)
            {
                dependenceInfoMap.Remove(path);

                if (isDirectInBundleAsset(path, config))
                {
                    realModifiedAssetPaths.Add(path);
                }

                HashSet<string> deletePaths = new HashSet<string>();
                foreach (var entry in dependenceInfoMap)
                {
                    if (entry.Value.directDependencies.Contains(path))
                    {
                        deletePaths.Add(entry.Key);
                        if (!modifiedPathSet.Contains(entry.Key))
                        {
                            realModifiedAssetPaths.Add(entry.Key);
                        }
                    }
                }

                foreach (var p in deletePaths)
                {
                    dependenceInfoMap.Remove(p);
                }
            }

            UpdateDependenceInfoByPaths(realModifiedAssetPaths, dependenceInfoMap);

            HashSet<string> deleteAssetSet = new HashSet<string>();
            foreach (var entry in dependenceInfoMap)
            {
                if (!File.Exists(entry.Key))
                {
                    deleteAssetSet.Add(entry.Key);
                    continue;
                }
            }

            foreach (var path in deleteAssetSet)
            {
                dependenceInfoMap.Remove(path);
            }
        }
    }

    static void ResetShaderDefaultTexturePropertyNew(Dictionary<string, DependenceInfo> dependenceInfoMap)
    {
        HashSet<string> matSet = new HashSet<string>();
        HashSet<string> shaderSet = new HashSet<string>();
        foreach (var entry in dependenceInfoMap)
        {
            if (entry.Key.EndsWith(".mat"))
            {
//                if (entry.Key.StartsWith(ShaderTool.assetBundleShaderVariantMatDir))
//                {
                    matSet.Add(entry.Key);
//                }
//                else
//                {
//                    if (!File.Exists(entry.Key))
//                    {
//                        deleteAssetSet.Add(entry.Key);
//                    }
//                    else
//                    {
//                        matSet.Add(entry.Key);
//                    }
//                }
            }
            else if (entry.Key.EndsWith(".shader"))
            {
//                if (!CommonUtil.Exists(entry.Key))
//                {
//                    throw new System.Exception("shader file don't exits: " + entry.Key);
//                }
                ShaderImporter shaderImporter = ShaderImporter.GetAtPath(entry.Key) as ShaderImporter;
                Shader shader = shaderImporter.GetShader();

                bool found = false;
                int count = ShaderUtil.GetPropertyCount(shader);
                for (int i = 0; i < count; i++)
                {
                    ShaderUtil.ShaderPropertyType propType = ShaderUtil.GetPropertyType(shader, i);
                    if (propType == ShaderUtil.ShaderPropertyType.TexEnv)
                    {
                        string propName = ShaderUtil.GetPropertyName(shader, i);
                        if (shaderImporter.GetDefaultTexture(propName) != null)
                        {
                            shaderImporter.SetDefaultTextures(new string[0], new Texture[0]);
                            found = true;
                            break;
                        }
                    }

                    if (found)
                    {
                        break;
                    }
                }

                if (found)
                {
                    shaderImporter.SaveAndReimport();
                    shaderSet.Add(entry.Key);
                }
            }
        }

        AssetDatabase.Refresh();


        foreach(var path in matSet)
        {
            Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
            if (mat == null)
            {
                throw new System.Exception("load material failed: " + path);
            }

            ShaderTool.CreateShaderVariant(mat, null, false, ShaderTool.assetBundleShaderVariantMatDir);
            ShaderTool.CreateShaderVariant(mat, null, true, ShaderTool.assetBundleShaderVariantMatDir);
        }

        DependenceInfo.ResetPaths(dependenceInfoMap, shaderSet);

        foreach (var p in shaderSet)
        {
            DependenceInfo.GetDependencies(dependenceInfoMap, p, false);
        }
    }

    static void UpdateShaderBundleAssetPaths(BundleConfig config, Dictionary<string, DependenceInfo> dependenceInfoMap, Dictionary<Bundle, HashSet<string>> bundleAssetPathSetMap)
    {
        BundleLayer bundleLayer = config.bundles;
        Dictionary<string, Bundle>.Enumerator enumerator = bundleLayer.GetEnumerator();

        while (enumerator.MoveNext())
        {
            Bundle bundle = enumerator.Current.Value;
            if (bundle.type == BundleType.shader)
            {
                string[] dirs = bundle.dirs;
                HashSet<string> shaderSet = new HashSet<string>(dirs);

                foreach (var entry in dependenceInfoMap)
                {
                    string assetPath = entry.Key;
                    if (!assetPath.EndsWith(".shader"))
                    {
                        continue;
                    }

                    bool found = false;
                    for (int k = 0; k < dirs.Length; k++)
                    {
                        if (assetPath.StartsWith(dirs[k]))
                        {
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        shaderSet.Add(assetPath);
                    }
                }

                bundle.dirs = shaderSet.ToArray();

                bundleAssetPathSetMap.Remove(bundle);
                break;
            }
        }
    }

//    public static void ResetShaderDefaultTextureProperty(BundleConfig config, Dictionary<Bundle, HashSet<string>> bundleAssetPathSetMap)
//    {
//        Dictionary<string, DependenceInfo> dependenceInfoMap = new Dictionary<string, DependenceInfo>();
//
//        {
//            BundleLayer bundleLayer = config.bundles;
//            Dictionary<string, Bundle>.Enumerator enumerator = bundleLayer.GetEnumerator();
//
//            while (enumerator.MoveNext())
//            {
//                Bundle bundle = enumerator.Current.Value;
//                if (bundle.type == BundleType.shader)
//                {
//                    string[] dirs = bundle.dirs;
//                    HashSet<string> assetSet = new HashSet<string>();
//
//                    foreach (var entry in config.bundles)
//                    {
//                        if (bundle == entry.Value)
//                        {
//                            continue;
//                        }
//
//                        HashSet<string> assetPathSet = GetBundleAssetPaths(entry.Value, bundleAssetPathSetMap);
//                        assetSet.Add(assetPathSet);
//
//                        foreach (var a in assetPathSet)
//                        {
//                            HashSet<string> dependencies = DependenceInfo.GetDependencies(dependenceInfoMap, a, true);
//                            assetSet.Add(dependencies);
//                        }
//                    }
//
//                    HashSet<string> shaderSet = new HashSet<string>(dirs);
//
//                    foreach (string assetPath in assetSet)
//                    {
//                        if (!assetPath.EndsWith(".shader"))
//                        {
//                            continue;
//                        }
//
//                        bool found = false;
//                        for (int k = 0; k < dirs.Length; k++)
//                        {
//                            if (assetPath.StartsWith(dirs[k]))
//                            {
//                                found = true;
//                                break;
//                            }
//                        }
//
//                        if (!found)
//                        {
//                            shaderSet.Add(assetPath);
//                        }
//                    }
//
//                    bundle.dirs = shaderSet.ToArray();
//
//                    HashSet<string> shaderSet2 = GetBundleAssetPaths(bundle, bundleAssetPathSetMap);
//                    foreach (var shaderPath in shaderSet2)
//                    {
//                        if (shaderPath.EndsWith(".shader"))
//                        {
//                            ShaderImporter shaderImporter = ShaderImporter.GetAtPath(shaderPath) as ShaderImporter;
//                            shaderImporter.SetDefaultTextures(new string[0], new Texture[0]);
//                            shaderImporter.SaveAndReimport();
//                        }
//                    }
//
//                    AssetDatabase.Refresh();
//
//                    foreach (string assetPath in assetSet)
//                    {
//                        if (!assetPath.EndsWith(".mat"))
//                        {
//                            continue;
//                        }
//
//                        Material mat = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
//                        ShaderTool.CreateShaderVariant(mat, null, false, ShaderTool.assetBundleShaderVariantMatDir);
//                        ShaderTool.CreateShaderVariant(mat, null, true, ShaderTool.assetBundleShaderVariantMatDir);
//                    }
//
//                    AssetDatabase.SaveAssets();
//                    AssetDatabase.Refresh();
//                }
//            }
//
//            dependenceInfoMap.Clear();
//        }
//    }





    static HashSet<string> GetNoInBundleDependencies(string assetPath, Dictionary<string, DependenceInfo> dependenceInfoMap, Dictionary<string, Dictionary<string, AssetBundleInfo>> bundleMap)
    {
        HashSet<string> result = new HashSet<string>();
        HashSet<string> directDependencies = DependenceInfo.GetDependencies(dependenceInfoMap, assetPath, false);

        Queue<string> directDependencieQueue = new Queue<string>(directDependencies);
        while (directDependencieQueue.Count > 0)
        {
            string directDependencePath = directDependencieQueue.Dequeue();

            bool found = false;
            foreach (var entry3 in bundleMap)
            {
                foreach (var entry4 in entry3.Value)
                {
                    if (entry4.Value.assetPaths.Contains(directDependencePath))
                    {
                        found = true;
                        break;
                    }
                }

                if (found)
                {
                    break;
                }
            }

            if (!found)
            {
                result.Add(directDependencePath);

                HashSet<string> directDependencies2 = DependenceInfo.GetDependencies(dependenceInfoMap, directDependencePath, false);

                foreach (var d in directDependencies2)
                {
                    directDependencieQueue.Enqueue(d);
                }
            }
        }

        return result;
    }

    static void FindAssetSharedBundles(string assetPath, Dictionary<string, HashSet<string>> reverseDependenceMap, Dictionary<string, HashSet<string>> assetSharedBundleMap, Dictionary<string, string> assetPathBundleMap)
    {
        if (assetPath != null)
        {
            if (assetSharedBundleMap.ContainsKey(assetPath))
            {
                return;
            }

            if (reverseDependenceMap.ContainsKey(assetPath))
            {
                HashSet<string> result = new HashSet<string>();
                HashSet<string> parents = reverseDependenceMap[assetPath];
                foreach (var p in parents)
                {
                    FindAssetSharedBundles(p, reverseDependenceMap, assetSharedBundleMap, assetPathBundleMap);
                    HashSet<string> s = assetSharedBundleMap[p];
                    result.Add(s);
                }

                assetSharedBundleMap.Add(assetPath, result);
            }
            else //root asset
            {
                HashSet<string> h = new HashSet<string>();
                h.Add(assetPathBundleMap[assetPath]);
                assetSharedBundleMap.Add(assetPath, h);
            }
        }
        else
        {
            foreach (var entry in reverseDependenceMap)
            {
                FindAssetSharedBundles(entry.Key, reverseDependenceMap, assetSharedBundleMap, assetPathBundleMap);
            }
        }
    }

    static void GetMainBundleMap(BundleConfig config, Dictionary<string, Bundle> newBundles, HashSet<string> removeBundles, Dictionary<Bundle, HashSet<string>> bundleAssetPathSetMap, ref Dictionary<string, Dictionary<string, AssetBundleInfo>> bundleMap)
    {
        BundleLayer bundleLayer = config.bundles;
        foreach(var entry in bundleLayer)
        {
            string bundleName = entry.Key;
            Bundle bundle = entry.Value;
            if (bundle.type == BundleType.sprite_sheet)
            {
                removeBundles.Add(bundleName);

                if(EditorSwitchConfig.spriteSheetMode == SpriteSheetMode.texturepacker)
                {
                    Debug.Log("use sprite sheet bundle");
                    Dictionary<string, AssetBundleInfo> tempMap = new Dictionary<string, AssetBundleInfo>();
                    for (int j = 0; j < bundle.dirs.Length; j++)
                    {
                        string dir = bundle.dirs[j];
                        string[] spriteSheetDirPaths = Directory.GetDirectories(dir, "*", SearchOption.TopDirectoryOnly);
                        for (int k = 0; k < spriteSheetDirPaths.Length; k++)
                        {
                            string spriteSheetDirPath = spriteSheetDirPaths[k];
                            string dirName = System.IO.Path.GetFileName(spriteSheetDirPath);

                            Bundle newBundle = new Bundle();
                            newBundle.type = BundleType.normal;
                            newBundle.local_type = bundle.local_type;
                            newBundle.dirs = new string[]{ dir + "/" + dirName };
                            if (newBundles.ContainsKey(dirName))
                            {
                                throw new System.Exception("bundle repeated: " + dirName + "  " + dir);
                            }
                            newBundles.Add(dirName, newBundle);

                            AssetBundleInfo info = new AssetBundleInfo();
                            info.bundle = newBundle;
                            tempMap.Add(dirName, info);
                        }

                        bundleMap.Add(bundleName, tempMap);
                    }
                }
                else
                {
                    Dictionary<string, AssetBundleInfo> tempMap = new Dictionary<string, AssetBundleInfo>();

                    SpriteSheetConfig spriteSheetConfig = ConfigTool.CreateRawSpriteSheetConfig(ResourcesEx.BundleResDir);
                    foreach(var entry2 in spriteSheetConfig)
                    {
                        var spInfo = entry2.Value;
                        if(!spInfo.isLeaf())
                        {
                            continue;
                        }

                        var dirs = spInfo.dirs;
                        string spriteSheetName = entry2.Key;

                        if (newBundles.ContainsKey(spriteSheetName))
                        {
                            throw new System.Exception("bundle repeated: " + spriteSheetName);
                        }

                        Bundle newBundle = new Bundle();
                        newBundle.type = BundleType.normal;
                        newBundle.local_type = bundle.local_type;
                        List<string> dirList = new List<string>();
                        for(int i = 0; i < dirs.Length; i++)
                        {
                            dirList.Add(ResourcesEx.BundleResDir + "/" + dirs[i]);
                        }

                        if(EditorSwitchConfig.spriteSheetMode == SpriteSheetMode.spriteatlas)
                        {
                            string spriteAtlasPath = ResourcesEx.BundleResDir + "/" + ResourcesEx.SpriteAtlasDir + "/" + spriteSheetName + ".spriteatlas";
                            dirList.Add(spriteAtlasPath);
                        }

                        newBundle.dirs = dirList.ToArray();
                        
                        newBundles.Add(spriteSheetName, newBundle);

                        AssetBundleInfo info = new AssetBundleInfo();
                        info.bundle = newBundle;
                        tempMap.Add(spriteSheetName, info);
                    }

                    bundleMap.Add(bundleName, tempMap);
                }
            }
            else if(bundle.type == BundleType.scene)
            {
                removeBundles.Add(bundleName);

                HashSet<string> assetPaths = GetBundleAssetPaths(bundle, bundleAssetPathSetMap);

                Dictionary<string, AssetBundleInfo> tempMap = new Dictionary<string, AssetBundleInfo>();

                foreach (var scenePath in assetPaths)
                {
                    Bundle newBundle = new Bundle();
                    newBundle.type = BundleType.scene;
                    newBundle.local_type = bundle.local_type;
                    newBundle.dirs = new string[]{ scenePath };
                    string sceneName = Path.GetFileNameWithoutExtension(scenePath);
                    newBundles.Add(sceneName + "_scene", newBundle);

                    AssetBundleInfo info = new AssetBundleInfo();
                    info.bundle = newBundle;
                    tempMap.Add(sceneName, info);
                }

                bundleMap.Add(bundleName, tempMap);
            }
            else
            {
                AssetBundleInfo info = new AssetBundleInfo();
                info.bundle = bundle;
                Dictionary<string, AssetBundleInfo> tempMap = new Dictionary<string, AssetBundleInfo>();
                tempMap.Add(bundleName, info);
                bundleMap.Add(bundleName, tempMap);
            }
        }
    }

    static void UpdateDependenceInfoByPaths(HashSet<string> assetPathSet, Dictionary<string, DependenceInfo> dependenceInfoMap)
    {
        HashSet<string> addedQueueSet = new HashSet<string>();
        Queue<string> q = new Queue<string>(assetPathSet);
        while (q.Count > 0)
        {
            string assetPath = q.Dequeue();

            if (addedQueueSet.Contains(assetPath))
            {
                continue;
            }
            addedQueueSet.Add(assetPath);

            HashSet<string> directDependencies = DependenceInfo.GetDependencies(dependenceInfoMap, assetPath, false);

            if (directDependencies != null)
            {
                foreach (var directDependence in directDependencies)
                {
                    if (addedQueueSet.Contains(directDependence))
                    {
                        continue;
                    }

                    q.Enqueue(directDependence);
                }
            }
        }
    }

    static Dictionary<string, HashSet<string>> GetIndirectIncludedAssetPathsMap(Dictionary<string, Dictionary<string, AssetBundleInfo>> bundleMap, Dictionary<string, DependenceInfo> dependenceInfoMap, out HashSet<string> addedQueueSet)
    {
        Dictionary<string, HashSet<string>> indirectIncludedAssetPathsMap = new Dictionary<string, HashSet<string>>();

        addedQueueSet = new HashSet<string>();
        foreach (var entry in bundleMap)
        {
            foreach (var entry2 in entry.Value)
            {
                string key = entry2.Key;
                AssetBundleInfo info = entry2.Value;

                Queue<string> q = new Queue<string>(info.assetPaths);


                while (q.Count > 0)
                {
                    string assetPath = q.Dequeue();

                    if (addedQueueSet.Contains(assetPath))
                    {
                        continue;
                    }
                    addedQueueSet.Add(assetPath);

                    HashSet<string> directDependencies = DependenceInfo.GetDependencies(dependenceInfoMap, assetPath, false);
                    if (directDependencies == null)
                    {
                        throw new System.Exception("GetDependencies failed: " + assetPath);
                    }

                    HashSet<string> indirectIncludedAssetPaths = new HashSet<string>();
                    foreach (var directDependence in directDependencies)
                    {
                        if (directDependence.EndsWith(".cs") || directDependence.EndsWith(".js"))
                        {
                            continue;
                        }

                        bool found = false;
                        foreach (var entry3 in bundleMap)
                        {
                            foreach (var entry4 in entry3.Value)
                            {
                                if (entry4.Value.assetPaths.Contains(directDependence))
                                {
                                    found = true;
                                    break;
                                }
                            }

                            if (found)
                            {
                                break;
                            }
                        }

                        if (!found)
                        {
                            indirectIncludedAssetPaths.Add(directDependence);

                            q.Enqueue(directDependence);
                        }
                    }

                    if (indirectIncludedAssetPaths.Count > 0)
                    {
                        indirectIncludedAssetPathsMap.Add(assetPath, indirectIncludedAssetPaths);
                    }
                }
            }
        }

        return indirectIncludedAssetPathsMap;
    }

    static Dictionary<string, HashSet<string>> GetAssetShareInfo(Dictionary<string, Dictionary<string, AssetBundleInfo>> bundleMap, Dictionary<string, string> assetPathBundleMap, Dictionary<string, DependenceInfo> dependenceInfoMap, out Dictionary<string, HashSet<string>> autoIncludeAssetMap)
    {
        HashSet<string> addedQueueSet = null;
        Dictionary<string, HashSet<string>> indirectIncludedAssetPathsMap = GetIndirectIncludedAssetPathsMap(bundleMap, dependenceInfoMap, out addedQueueSet);


        Dictionary<string, HashSet<string>> reverseDirectDependenceMap = new Dictionary<string, HashSet<string>>();
        foreach (var d in indirectIncludedAssetPathsMap)
        {
            foreach (var e in d.Value)
            {
                HashSet<string> reverseSet = null;
                if (!reverseDirectDependenceMap.TryGetValue(e, out reverseSet))
                {
                    reverseSet = new HashSet<string>();
                    reverseDirectDependenceMap.Add(e, reverseSet);
                }

                reverseSet.Add(d.Key);
            }
        }


        autoIncludeAssetMap = new Dictionary<string, HashSet<string>>();
        foreach (var assetPath in addedQueueSet)
        {
            autoIncludeAssetMap.Add(assetPath, new HashSet<string>());
        }

        while (true)
        {
            Queue<string> q2 = new Queue<string>();

            //find all single depended asset to add queue
            foreach (var d in reverseDirectDependenceMap)
            {
                if (!assetPathBundleMap.ContainsKey(d.Key) && d.Value.Count <= 1)
                {
                    q2.Enqueue(d.Key);
                }
            }

            if (q2.Count <= 0)
            {
                break;
            }

            while (q2.Count > 0)
            {
                string child = q2.Dequeue();

                HashSet<string> tempSet = reverseDirectDependenceMap[child];
                HashSet<string>.Enumerator e = tempSet.GetEnumerator();
                e.MoveNext();
                string parent = e.Current;




                //child auto included to parent asset
                HashSet<string> tempSet3 = null;
                if (!autoIncludeAssetMap.TryGetValue(parent, out tempSet3))
                {
                    tempSet3 = new HashSet<string>();
                    autoIncludeAssetMap.Add(parent, tempSet3);
                }

                tempSet3.Add(child);

                HashSet<string> tempSet2 = null;
                if (autoIncludeAssetMap.TryGetValue(child, out tempSet2))
                {
                    tempSet3.Add(tempSet2);
                    autoIncludeAssetMap.Remove(child);
                }





                //modify dependence, use parent asset to instead of child asset
                reverseDirectDependenceMap.Remove(child);
                foreach (var entry in reverseDirectDependenceMap)
                {
                    if (entry.Value.Contains(child))
                    {
                        entry.Value.Remove(child);
                        entry.Value.Add(parent);
                    }
                }
            }
        }

        Dictionary<string, HashSet<string>> assetSharedBundleMap = new Dictionary<string, HashSet<string>>();
        FindAssetSharedBundles(null, reverseDirectDependenceMap, assetSharedBundleMap, assetPathBundleMap);

        return assetSharedBundleMap;
    }

    static void CreateNewCommonBundleInfo(Dictionary<string, Dictionary<string, AssetBundleInfo>> bundleMap, Dictionary<string, Bundle> newBundles, Dictionary<string, HashSet<string>> shareBundleMap, Dictionary<string, string> assetPathBundleMap, Dictionary<string, int> assetPathSizeMap, Dictionary<string, HashSet<string>> autoIncludedAssetMap, HashSet<string> localBundleFileSet, Dictionary<string, DependenceInfo> dependenceInfoMap, BundleConfig oldEditorBundleConfig, HashSet<string> modifiedPathSet)
    {
        Dictionary<string, HashSet<string>> newCommonBundleAssetPathSetMap = new Dictionary<string, HashSet<string>>();

        foreach(var entry in shareBundleMap)
        {
            if (assetPathBundleMap.ContainsKey(entry.Key))
            {
                continue;
            }

            string newBundleName = "c_";
            string[] bundleNames = entry.Value.ToArray();
            System.Array.Sort(bundleNames);
            for (int i = 0; i < bundleNames.Length; i++)
            {
                string bundleName = bundleNames[i];
                newBundleName += "_" + bundleName;
            }

//            if(!SwitchConfig.encodeBundleName)
//            {
//                newBundleName = "ce__" + NewBundleManager.ConvertToHashName(newBundleName);
//            }

            HashSet<string> t = null;
            if (!newCommonBundleAssetPathSetMap.TryGetValue(newBundleName, out t))
            {
                t = new HashSet<string>();
                newCommonBundleAssetPathSetMap.Add(newBundleName, t);
            }

            t.Add(entry.Key);
        }

        foreach (var entry in newCommonBundleAssetPathSetMap)
        {
            List<string> guidList = new List<string>();
            foreach (var assetPath in entry.Value)
            {
                string guid = AssetDatabase.AssetPathToGUID(assetPath);
                guidList.Add(guid);
            }
            guidList.Sort();

            string newBundleName = "c_";
            for (int i = 0; i < guidList.Count; i++)
            {
                string guid = guidList[i];
                newBundleName += "_" + guid;
            }

            if(!EditorSwitchConfig.encodeBundleName)
            {
                newBundleName = "ce__" + NewBundleManager.ConvertToHashName(newBundleName);
            }

            Dictionary<string, AssetBundleInfo> tempMap = SplitBundle(newBundleName, entry.Value, assetPathSizeMap, autoIncludedAssetMap, localBundleFileSet, dependenceInfoMap, LocalType.none, prefabCountPerbundle, defaultBundleSize * 1024 * 1024, oldEditorBundleConfig, modifiedPathSet);
            foreach (var entry2 in tempMap)
            {
                entry2.Value.bundle.is_dependenced = true;
                newBundles.Add(entry2.Key, entry2.Value.bundle);
            }

            bundleMap.Add(entry.Key, tempMap);
        }
    }

    static void CreatePrefabBundles(Dictionary<string, Dictionary<string, AssetBundleInfo>> bundleMap, Dictionary<string, Bundle> newBundles, HashSet<string> removeBundles, BundleConfig config, Dictionary<Bundle, HashSet<string>> bundleAssetPathSetMap, Dictionary<string, int> assetPathSizeMap, Dictionary<string, HashSet<string>> autoIncludedAssetMap, HashSet<string> localBundleFileSet, BundleConfig oldEditorBundleConfig, HashSet<string> modifiedPathSet, Dictionary<string, DependenceInfo> dependenceInfoMap)
    {
        foreach (var entry in config.bundles)
        {
            string bundleName = entry.Key;
            Bundle bundle = entry.Value;

            if(bundle.type == BundleType.prefab)
            {
                removeBundles.Add(bundleName);

                HashSet<string> assetSet = GetBundleAssetPaths(bundle, bundleAssetPathSetMap);

                int prefabCount = prefabCountPerbundle;
                if (bundle.max_asset_num_per_bundle > 0)
                {
                    prefabCount = bundle.max_asset_num_per_bundle;
                }

                int maxBundleSize = defaultBundleSize;
                if (bundle.max_size_per_bundle > 0)
                {
                    maxBundleSize = bundle.max_size_per_bundle;
                }

                if (bundle.split_by_scene && getSceneAllPrefabPaths != null)
                {
                    Dictionary<string, HashSet<string>> sceneAssetPathMap = new Dictionary<string, HashSet<string>>();
                    
                    foreach (var entry2 in bundleMap)
                    {
                        foreach (var entry3 in entry2.Value)
                        {
                            if (entry3.Value.bundle.type != BundleType.scene)
                            {
                                continue;
                            }


                            foreach (var assetPath in entry3.Value.assetPaths)
                            {
                                HashSet<string> prefabPaths = getSceneAllPrefabPaths(assetPath);
                                HashSet<string> newPrefabPaths = new HashSet<string>();
                                foreach (string p in prefabPaths)
                                {
                                    newPrefabPaths.Add(ResourcesEx.BundleResDir + "/" + p + ".prefab");
                                }

                                string sceneName = System.IO.Path.GetFileNameWithoutExtension(assetPath);

                                sceneAssetPathMap.Add(sceneName, newPrefabPaths);
                            }
                        }
                    }

                    Dictionary<string, HashSet<string>> assetPathScenesMap = new Dictionary<string, HashSet<string>>();
                    foreach (var entry2 in sceneAssetPathMap)
                    {
                        foreach (var assetPath in entry2.Value)
                        {
                            HashSet<string> sceneSet = null;
                            if(!assetPathScenesMap.TryGetValue(assetPath, out sceneSet))
                            {
                                sceneSet = new HashSet<string>();
                                assetPathScenesMap.Add(assetPath, sceneSet);
                            }

                            sceneSet.Add(entry2.Key);
                        }
                    }

                    CommonUtil.ObjectToJsonFile(assetPathScenesMap, resPath + "/" + bundleName + "_split_by_scene.json");


//                    Dictionary<string, HashSet<string>> subBundleAssetPathMap = new Dictionary<string, HashSet<string>>();
                    Dictionary<string, string> assetPathSubBundleMap = new Dictionary<string, string>();
                    foreach (var entry2 in assetPathScenesMap)
                    {
                        string[] listNames = entry2.Value.ToArray();
                        System.Array.Sort(listNames);

                        string subBundleName = "";
                        for (int i = 0; i < listNames.Length; i++)
                        {
                            if (i == 0)
                            {
                                subBundleName = listNames[i];
                            }
                            else
                            {
                                subBundleName += "_" + listNames[i];
                            }
                        }

                        if(listNames.Length > 1 && !EditorSwitchConfig.encodeBundleName)
                        {
                            subBundleName = "ce__" + NewBundleManager.ConvertToHashName(subBundleName);
                        }

//                        HashSet<string> pathSet = null;
//                        if (!subBundleAssetPathMap.TryGetValue(subBundleName, out pathSet))
//                        {
//                            pathSet = new HashSet<string>();
//                            subBundleAssetPathMap.Add(subBundleName, pathSet);
//                        }

//                        pathSet.Add(entry2.Key);

                        assetPathSubBundleMap.Add(entry2.Key, subBundleName);
                    }


                    Dictionary<string, HashSet<string>> newSubBundleAssetPathMap = new Dictionary<string, HashSet<string>>();
                    foreach (var path in assetSet)
                    {
                        string subBundleName = null;
                        if (!assetPathSubBundleMap.TryGetValue(path, out subBundleName))
                        {
                            subBundleName = "remain_sub_bundle";
                        }

                        HashSet<string> assetPathSet = null;
                        if (!newSubBundleAssetPathMap.TryGetValue(subBundleName, out assetPathSet))
                        {
                            assetPathSet = new HashSet<string>();
                            newSubBundleAssetPathMap.Add(subBundleName, assetPathSet);
                        }

                        assetPathSet.Add(path);
                    }

                    Dictionary<string, AssetBundleInfo> newBundleMap = new Dictionary<string, AssetBundleInfo>();
                    foreach (var entry2 in newSubBundleAssetPathMap)
                    {
                        Dictionary<string, AssetBundleInfo> tempMap = SplitBundle(bundleName + "__" + entry2.Key, entry2.Value, assetPathSizeMap, autoIncludedAssetMap, localBundleFileSet, dependenceInfoMap, bundle.local_type, prefabCount, maxBundleSize * 1024 * 1024, oldEditorBundleConfig, modifiedPathSet);
                        foreach (var entry3 in tempMap)
                        {
                            newBundleMap.Add(entry3.Key, entry3.Value);
                        }
                    }

                    bundleMap.Remove(bundleName);
                    bundleMap.Add(bundleName, newBundleMap);
                    Dictionary<string, AssetBundleInfo>.Enumerator e = newBundleMap.GetEnumerator();
                    while (e.MoveNext())
                    {
                        newBundles.Add(e.Current.Key, e.Current.Value.bundle);
                    }
                }
                else
                {
                    Dictionary<string, AssetBundleInfo> tempMap = SplitBundle(bundleName, assetSet, assetPathSizeMap, autoIncludedAssetMap, localBundleFileSet, dependenceInfoMap, bundle.local_type, prefabCount, maxBundleSize * 1024 * 1024, oldEditorBundleConfig, modifiedPathSet);
                    bundleMap.Remove(bundleName);
                    bundleMap.Add(bundleName, tempMap);
                    Dictionary<string, AssetBundleInfo>.Enumerator e = tempMap.GetEnumerator();
                    while (e.MoveNext())
                    {
                        newBundles.Add(e.Current.Key, e.Current.Value.bundle);
                    }
                }
            }
        }
    }

    static HashSet<string> GetModifiedPathSet(int oldVersion, string svnOtherParams)
    {
        HashSet<string> modifiedPathSet = new HashSet<string>();

        HashSet<string> tempModifiedPathSet = SvnTool.GetModifiedFileSet(oldVersion, svnOtherParams);
        foreach (var s in tempModifiedPathSet)
        {
            if (s.EndsWith(".meta"))
            {
                modifiedPathSet.Add(s.Substring(0, s.Length - 5));
            }
            else
            {
                modifiedPathSet.Add(s);
            }
        }

            //            string output = null;
            //            string err = CommandLineTool.cmdWithOutput("svn", "info --show-item last-changed-revision", ref output);
            //            if (!string.IsNullOrEmpty(err))
            //            {
            //                throw new System.Exception(err);
            //            }
            //            int lastChangedVersion = int.Parse(output);
            //
            //
            //
            //            string output2 = null;
            //            string err2 = CommandLineTool.cmdWithOutput("svn", "info --show-item relative-url", ref output2);
            //            if (!string.IsNullOrEmpty(err2))
            //            {
            //                throw new System.Exception(err2);
            //            }
            //            output2 = output2.Replace("\n", "");
            //            string needRemovePath = output2.Substring(1, output2.Length - 1) + "/";
            //
            //
            //
            //
            //            int oldConfigVersion = oldVersion;
            //
            ////            Debug.LogError("test svn2: " + lastChangedVersion + "  " + oldConfigVersion);
            //            if (oldConfigVersion != lastChangedVersion)
            //            {
            //                if (lastChangedVersion > oldConfigVersion)
            //                {
            //                    oldConfigVersion++;
            //                }
            //                else
            //                {
            //                    oldConfigVersion--;
            //                }
            //
            //                err = CommandLineTool.cmdWithOutput("svn", "log -r " + lastChangedVersion + ":" + oldConfigVersion + " . -v", ref output);
            //
            //
            //
            //                if (!string.IsNullOrEmpty(err))
            //                {
            //                    throw new System.Exception(err);
            //                }
            //
            ////                Debug.LogError("test svn: " + output);
            //
            //                System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(@"^[ ]+.[ ]+(.*)");
            //
            //
            //                System.IO.StringReader strReader = new System.IO.StringReader(output);
            //                while (true)
            //                {
            //                    string line = strReader.ReadLine();
            //                    if (line == null)
            //                    {
            //                        break;
            //                    }
            //
            //                    System.Text.RegularExpressions.Match match = regex.Match(line);
            //                    if (match.Success)
            //                    {
            //                        string assetPath = match.Groups[1].Value;
            //                        assetPath = assetPath.Substring(needRemovePath.Length, assetPath.Length - needRemovePath.Length);
            //                        if (assetPath.EndsWith(".meta"))
            //                        {
            //                            assetPath = assetPath.Substring(0, assetPath.Length - 5);
            //                        }
            //
            //                        modifiedPathSet.Add(assetPath);
            //                    }
            //                }
            //            }

        HashSet<string> tempSet = new HashSet<string>(modifiedPathSet);
        foreach (var p in tempSet)
        {
            if (p.EndsWith(".meta"))
            {
                string temp = p.Substring(0, p.Length - ".meta".Length);
                modifiedPathSet.Add(temp);
                modifiedPathSet.Remove(p);
            }
        }

        return modifiedPathSet;
    }

    static BundleConfig GetPlatformRawBundleConfig(string filePath, BuildTarget target)
    {
        
        string str = CommonUtil.fileToString(filePath);
        string platformStr = target.getPlatform().ToString();
        string ConfigStr = str.Replace("${platform}", platformStr);
        BundleConfig config = JsonConvert.DeserializeObject<BundleConfig>(ConfigStr);
        return config;
    }

    static Dictionary<string, int> GetAssetSizeMap(string sizeConfigPath, HashSet<string> modifiedPathSet, Dictionary<string, HashSet<string>> autoIncludedAssetMap, out Dictionary<string, int> assetSizeMap)
    {
        assetSizeMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, int>>(sizeConfigPath);
        if (assetSizeMap == null)
        {
            assetSizeMap = new Dictionary<string, int>();
        }

        foreach (var assetPath in modifiedPathSet)
        {
            assetSizeMap.Remove(assetPath);
        }

        Dictionary<string, int> includeAutoIncludeAssetSizeMap = new Dictionary<string, int>();

        foreach (var entry in autoIncludedAssetMap)
        {
            int sumSize = 0;

            int texSize = 0;
            if (!assetSizeMap.TryGetValue(entry.Key, out texSize))
            {
                Texture mainTex = AssetDatabase.LoadAssetAtPath<Texture>(entry.Key);
                if (mainTex != null)
                {
                    texSize = mainTex.GetTextureSize();
                }

                TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(entry.Key);
                if(textAsset != null)
                {
                    texSize = textAsset.bytes.Length;
                }

                assetSizeMap.Add(entry.Key, texSize);
            }

            sumSize += texSize;

            foreach (string d in entry.Value)
            {
                int texSize2 = 0;
                if (!assetSizeMap.TryGetValue(d, out texSize2))
                {
                    Texture tex = AssetDatabase.LoadAssetAtPath<Texture>(d);
                    if (tex != null)
                    {
                        texSize2 = tex.GetTextureSize();
                    }

                    TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(d);
                    if(textAsset != null)
                    {
                        texSize2 = textAsset.bytes.Length;
                    }


                    assetSizeMap.Add(d, texSize2);
                }

                sumSize += texSize2;
            }

            includeAutoIncludeAssetSizeMap.Add(entry.Key, sumSize);
        }
        
        return includeAutoIncludeAssetSizeMap;
    }

    public static void CreatePlatfromBundleConfig2(BuildTarget target, string outputPath, string configOutputPath, string svnOtherParams = null)
    {
        bool useOldConfig = false;
        BundleVersionConfig oldVersionConfig = null;
        if(useOldConfig)
        {
            oldVersionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(outputPath + "/" + BundleVersionManager.configName);
        }

        BundleConfig oldEditorBundleConfig = null;
        if(useOldConfig)
        {
            oldEditorBundleConfig = CommonUtil.ObjectFromJsonFile<BundleConfig>(configOutputPath + "/" + "config_editor.json");
        }
            
        HashSet<string> modifiedPathSet = new HashSet<string>();
        if (oldVersionConfig != null)
        {
            modifiedPathSet = GetModifiedPathSet(oldVersionConfig.version, svnOtherParams);
        }
       
        string filePath = resPath + "/" + "raw_" + BundleManager.configName + ".json";
        BundleConfig config = GetPlatformRawBundleConfig(filePath, target);

        Dictionary<string, DependenceInfo> dependenceInfoMap = null;
        if(useOldConfig)
        {
            dependenceInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, DependenceInfo>>(configOutputPath + "/" + "dependence_info.json");
        }

        Dictionary<Bundle, HashSet<string>> bundleAssetPathSetMap = new Dictionary<Bundle, HashSet<string>>();

        ConsumeTime.Log("UpdateDependenceInfoByBundleConfig", () =>
            {
                UpdateDependenceInfoByBundleConfig(config, bundleAssetPathSetMap, modifiedPathSet, ref dependenceInfoMap);
            });

        ConsumeTime.Log("ResetShaderDefaultTexturePropertyNew", () =>
            {
    //        ResetShaderDefaultTextureProperty(config, bundleAssetPathSetMap);
                ResetShaderDefaultTexturePropertyNew(dependenceInfoMap);
            });

        ConsumeTime.Log("UpdateShaderBundleAssetPaths", () =>
            {
                UpdateShaderBundleAssetPaths(config, dependenceInfoMap, bundleAssetPathSetMap);
            });


        HashSet<string> localBundleFileSet = new HashSet<string>();
        for (int j = 0; j < config.local_files.Length; j++)
        {
            string localFile = config.local_files[j];
            localBundleFileSet.Add(localFile);

            HashSet<string> ds = DependenceInfo.GetDependencies(dependenceInfoMap, localFile, true);
			if(ds != null)
			{
				localBundleFileSet.Add(ds);
			}
			else
			{
				
			}
        }
            
        Dictionary<string, Dictionary<string, AssetBundleInfo>> bundleMap = new Dictionary<string, Dictionary<string, AssetBundleInfo>>();

        HashSet<string> removeBundles = new HashSet<string>();
        Dictionary<string, Bundle> newBundles = new Dictionary<string, Bundle>();

        ConsumeTime.Log("GetMainBundleMap", () =>
            {
                GetMainBundleMap(config, newBundles, removeBundles, bundleAssetPathSetMap, ref bundleMap);
            });

        foreach (var entry in bundleMap)
        {
            foreach (var entry2 in entry.Value)
            {
                entry2.Value.assetPaths = GetBundleAssetPaths(entry2.Value.bundle, bundleAssetPathSetMap);
            }
        }


        Dictionary<string, string> assetPathBundleMap = new Dictionary<string, string>();

        foreach (var entry in bundleMap)
        {
            foreach (var entry2 in entry.Value)
            {
                string key = entry2.Key;
                AssetBundleInfo info = entry2.Value;
                foreach (var assetPath in info.assetPaths)
                {
                    if(useBundleNameToFindCommonAsset)
                    {
						if(assetPathBundleMap.ContainsKey(assetPath))
						{
							throw new System.Exception("repeat asset: " + assetPath + "  " + assetPathBundleMap[assetPath] + "  " + key);
						}
                        assetPathBundleMap.Add(assetPath, key);
                    }
                    else
                    {
                        string guid = AssetDatabase.AssetPathToGUID(assetPath);
                        assetPathBundleMap.Add(assetPath, guid);
                    }
                }
            }
        }
            
        Dictionary<string, HashSet<string>> autoIncludedAssetMap = null;
        Dictionary<string, HashSet<string>> assetSharedBundleMap = null;
        ConsumeTime.Log("GetAssetShareInfo", () =>
            {
                assetSharedBundleMap = GetAssetShareInfo(bundleMap, assetPathBundleMap, dependenceInfoMap, out autoIncludedAssetMap);
            });

        CommonUtil.ObjectToJsonFile(assetSharedBundleMap, resPath + "/" + "asset_share_bundles.json");


        Dictionary<string, int> assetSizeMap = null;
        Dictionary<string, int> includeAutoIncludedAssetSizeMap = null;
        ConsumeTime.Log("CreateAssetSizeMap", () =>
            {
                string sizeConfigPath = configOutputPath + "/" + "asset_size.json";
                includeAutoIncludedAssetSizeMap = GetAssetSizeMap(sizeConfigPath, modifiedPathSet, autoIncludedAssetMap, out assetSizeMap);
            });

        CommonUtil.ObjectToJsonFile(assetSizeMap, resPath + "/" + "asset_size.json");



        CommonUtil.ObjectToJsonFile(assetSharedBundleMap, resPath + "/" + "share_bundle.txt");

        ConsumeTime.Log("CreateNewCommonBundleInfo", () =>
            {
                CreateNewCommonBundleInfo(bundleMap, newBundles, assetSharedBundleMap, assetPathBundleMap, includeAutoIncludedAssetSizeMap, autoIncludedAssetMap, localBundleFileSet, dependenceInfoMap, oldEditorBundleConfig, modifiedPathSet); 
            });

        ConsumeTime.Log("CreatePrefabBundles", () =>
            {
                CreatePrefabBundles(bundleMap, newBundles, removeBundles, config, bundleAssetPathSetMap, includeAutoIncludedAssetSizeMap, autoIncludedAssetMap, localBundleFileSet, oldEditorBundleConfig, modifiedPathSet, dependenceInfoMap);
            });

        CommonUtil.ObjectToJsonFile(bundleMap, resPath + "/" + "bundle.txt");

        foreach (var bundleName in removeBundles)
        {
            config.bundles.Remove(bundleName);
        }

        foreach (var entry in newBundles)
        {
            config.bundles.Add(entry.Key.ToLower(), entry.Value);
        }


        CommonUtil.ObjectToJsonFile(autoIncludedAssetMap, resPath + "/" + "auto_included_asset.txt");





        {
            string outputFilePath = filePath.Replace("raw_", "");
            outputFilePath = System.IO.Path.ChangeExtension(outputFilePath, ".bytes");
            string outputFileDir = Path.GetDirectoryName(outputFilePath);
            Directory.CreateDirectory(outputFileDir);

            outputFilePath = outputFilePath.Replace(".bytes", "_editor.json");
            CommonUtil.ObjectToJsonFile(config, outputFilePath);
        }

        {
            Dictionary<string, string> assetToBundleMap = new Dictionary<string, string>();
            Dictionary<string, Dictionary<string, HashSet<string>>> bundleDependencies = new Dictionary<string, Dictionary<string, HashSet<string>>>();
            BundleLayer bundleLayer = config.bundles;
            foreach (var entry in bundleLayer)
            {
                string bundleName = entry.Key;

                string[] assetPaths = GetBundleAssetPaths(entry.Value, null).ToArray();
                HashSet<string> assetPathSet = new HashSet<string>(assetPaths);

                for(int i = 0; i < assetPaths.Length; i++)
                {
                    string assetPath = assetPaths[i];
                    HashSet<string> autoIncludeAssetSet = null;
                    autoIncludedAssetMap.TryGetValue(assetPath, out autoIncludeAssetSet);
                    assetPathSet.Add(autoIncludeAssetSet);
                }

                foreach(var assetPath in assetPathSet)
                {
                    assetToBundleMap.Add(assetPath, bundleName);
                }

                Dictionary<string, HashSet<string>> allAssetsDependenceSet = new Dictionary<string, HashSet<string>>();
                foreach(var a in assetPathSet)
                {
                    HashSet<string> allDependenceSet = new HashSet<string>();
                    string[] allDependencies = AssetDatabase.GetDependencies(new string[]{a}, true);
                    for(int i = 0; i < allDependencies.Length; i++)
                    {
                        string d = allDependencies[i];
                        if(!assetPathSet.Contains(d))
                        {
                            allDependenceSet.Add(d);
                        }
                    }

                    allAssetsDependenceSet.Add(a, allDependenceSet);
                }
                    
                bundleDependencies.Add(bundleName, allAssetsDependenceSet);
            }
                
            HashSet<string> notInBundleConfigAssetSet = new HashSet<string>();
            foreach(var entry in bundleDependencies)
            {
                foreach(var entry2 in entry.Value)
                {
                    HashSet<string> removeDependences = new HashSet<string>();
                    foreach(var d in entry2.Value)
                    {
                        if(!assetToBundleMap.ContainsKey(d))
                        {
                            notInBundleConfigAssetSet.Add(d);
                            removeDependences.Add(d);
                        }
                    }

                    foreach(var d in removeDependences)
                    {
                        entry2.Value.Remove(d);
                    }
                }
            }

            HashSet<string> logs = new HashSet<string>();
            CheckLoopForBundleConfig(null, bundleDependencies, assetToBundleMap, logs);

            string notInBundleConfigResLog = "notInBundleConfigAssetSet:\n";
            int notInBundleConfigResCount = 0;
            {
                string[] arr = notInBundleConfigAssetSet.ToArray();
                System.Array.Sort(arr, new SortByExtComparer());


                bool printScript = false;

                for(int i = 0; i < arr.Length && i < 10; i++)
                {
                    string p = arr[i];
                    if(!printScript && (p.EndsWith(".cs") || p.EndsWith(".js")))
                    {
                        continue;
                    }

                    notInBundleConfigResCount++;
                    notInBundleConfigResLog += p + "\n";
                }
            }

            {
                string logStr = "bundle loop:\n";

                if(logs.Count > 0)
                {
                    logStr = "bundle loop(" + logs.Count + "):\n";
                    int j = 0;
                    foreach(var log in logs)
                    {
                        if(j >= 10)
                        {
                            break;
                        }
                        logStr += log + "\n";
                        j++;
                    }
                }

                if(notInBundleConfigResCount > 0)
                {
                    logStr += notInBundleConfigResLog + "\n";
                }

                if(notInBundleConfigResCount > 0 || logs.Count > 0)
                {
                    throw new System.Exception(logStr);
                }
            }


        }

        ConsumeTime.Log("CreateConfig", () =>
        {
            HashSet<string> newModifiedAssetPaths = new HashSet<string>();
            foreach (var entry in autoIncludedAssetMap)
            {
                if (modifiedPathSet.Contains(entry.Key))
                {
                    newModifiedAssetPaths.Add(entry.Key);
                    continue;
                }

                foreach (var d in entry.Value)
                {
                    if (modifiedPathSet.Contains(d))
                    {
                        newModifiedAssetPaths.Add(entry.Key);
                        break;
                    }
                }
            }
                
            HashSet<string> modifiedBundles = new HashSet<string>();
            foreach (var entry in config.bundles)
            {
                HashSet<string> assetPaths = GetBundleAssetPaths(entry.Value, bundleAssetPathSetMap);
                foreach (var modifiedPath in newModifiedAssetPaths)
                {
                    assetPaths.Contains(modifiedPath);
                    modifiedBundles.Add(entry.Key);
                    break;
                }
            }
            CommonUtil.ObjectToJsonFile(modifiedBundles, resPath + "/" + "modified_bundle.txt");


            {
                {
                    HashSet<string> extSet = new HashSet<string>();
                    HashSet<string> scriptFilePathSet = new HashSet<string>();
                    BundleLayer bundleLayer = config.bundles;
                    foreach (var entry in bundleLayer)
                    {
                        HashSet<string> assetPaths = GetBundleAssetPaths(entry.Value, bundleAssetPathSetMap);
                        foreach (var assetPath in assetPaths)
                        {
                            HashSet<string> dependencies = DependenceInfo.GetDependencies(dependenceInfoMap, assetPath, true);

                            foreach (var d in dependencies)
                            {
                                if (d.EndsWith(".cs") || d.EndsWith(".js"))
                                {
                                    scriptFilePathSet.Add(d);
                                }

                                string ext = Path.GetExtension(d);
                                extSet.Add(ext);
                            }
                        }
                    }

                    SortedDictionary<string, List<KeyValuePair<string, string>>> monoScriptInfoMap = new SortedDictionary<string, List<KeyValuePair<string, string>>>();

                    foreach (var scriptPath in scriptFilePathSet)
                    {
                        MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(scriptPath);
                        if (script != null && script.GetClass() != null && (script.GetClass().IsSubclassOf(typeof(MonoBehaviour)) || script.GetClass().IsSubclassOf(typeof(ScriptableObject))))
                        {
                            MonoScriptDumper.DumpType(script.GetClass(), false, monoScriptInfoMap);
                        }
                        // else
                        // {
                        //     throw new System.Exception("load MonoScript failed: " + scriptPath);
                        // }
                    }

                    CommonUtil.ObjectToJsonFile(monoScriptInfoMap, resPath + "/" + "monoscript_info.txt");
                }



                {
                    HashSet<string> extSet = new HashSet<string>();
                    HashSet<string> scriptFilePathSet = new HashSet<string>();
                    string[] scriptPaths = System.IO.Directory.GetFiles("Assets", "*.cs", SearchOption.AllDirectories);
                    for(int i = 0; i < scriptPaths.Length; i++)
                    {
                        string d = scriptPaths[i];
                        scriptFilePathSet.Add(d);
                        string ext = Path.GetExtension(d);
                        extSet.Add(ext);
                    }
                    SortedDictionary<string, List<KeyValuePair<string, string>>> monoScriptInfoMap = new SortedDictionary<string, List<KeyValuePair<string, string>>>();

                    foreach (var scriptPath in scriptFilePathSet)
                    {
                        MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(scriptPath);
                        if (script != null && script.GetClass() != null && (script.GetClass().IsSubclassOf(typeof(MonoBehaviour)) || script.GetClass().IsSubclassOf(typeof(ScriptableObject))))
                        {
                            MonoScriptDumper.DumpType(script.GetClass(), false, monoScriptInfoMap);
                        }
                        // else
                        // {
                        //     throw new System.Exception("load MonoScript failed: " + scriptPath);
                        // }
                    }

                    CommonUtil.ObjectToJsonFile(monoScriptInfoMap, resPath + "/" + "project_monoscript_info.txt");
                }

                // {
                //     SortedDictionary<string, List<KeyValuePair<string, string>>> luaMonoScriptInfoMap = new SortedDictionary<string, List<KeyValuePair<string, string>>>();
                //     ToLuaMenu.BindType[] luaTypes = CustomSettings.customTypeList;
                //     for (int i = 0; i < luaTypes.Length; i++)
                //     {
                //         ToLuaMenu.BindType type = luaTypes[i];
                //         MonoScriptDumper.DumpType(type.type, true, luaMonoScriptInfoMap);
                // }

                //     CommonUtil.ObjectToJsonFile(luaMonoScriptInfoMap, resPath + "/" + "luascript_info.txt");
                // }

    //            CommonUtil.ObjectToJsonFile(scriptFilePathSet, "all_depended_script_files.txt");
    //            CommonUtil.ObjectToJsonFile(extSet, "all_ext_files.txt");
            }

            {
    //            Dictionary<string, HashSet<string>> dependenceMap = new Dictionary<string, HashSet<string>>();
                Dictionary<string, string> editorAssetToBundleMap = new Dictionary<string, string>();

                    BundleLayer bundleLayer = config.bundles;
                    foreach (var entry in bundleLayer)
                    {
                        HashSet<string> assetPaths = GetBundleAssetPaths(entry.Value, bundleAssetPathSetMap);
                        foreach(var assetPath in assetPaths)
                        {
                            if (editorAssetToBundleMap.ContainsKey(assetPath))
                            {
                                throw new System.Exception("repeat key: " + assetPath);
                            }

                            editorAssetToBundleMap.Add(assetPath, entry.Key);

    //                        HashSet<string> dependencies = DependenceInfo.GetDependencies(dependenceInfoMap, assetPath, true);
    //                        dependenceMap.Add(assetPath, dependencies);
                        }
                    }



//            Dictionary<string, string[]> newEditorAssetToBundleMap = new Dictionary<string, string[]>();
                AssetToBundleMap runtimeAssetToBundleMap = new AssetToBundleMap();
                Dictionary<string, string>.Enumerator enumerator = editorAssetToBundleMap.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    string path = enumerator.Current.Key;

    //                HashSet<string> dependeniedBundleNames = new HashSet<string>();
    //                HashSet<string> dependencies = dependenceMap[path];
    //                foreach(var dependence in dependencies)
    //                {
    //                    string bundleName = null;
    //                    if (editorAssetToBundleMap.TryGetValue(dependence, out bundleName))
    //                    {
    //                        dependeniedBundleNames.Add(bundleName);
    //                    }
    //                }
    //
    //                dependeniedBundleNames.Remove(enumerator.Current.Value);
    //                List<string> dependeniedBundleNameList = new List<string>();
    //                dependeniedBundleNameList.Add(enumerator.Current.Value);
    //                dependeniedBundleNameList.AddRange(dependeniedBundleNames);
    //                newEditorAssetToBundleMap.Add(path, dependeniedBundleNameList.ToArray());


                    string extName = Path.GetExtension(path);
                    string fileName = Path.GetFileNameWithoutExtension(path);
                    string dirName = Path.GetDirectoryName(path);
                    if (dirName.StartsWith(ResourcesEx.BundleResDir))
                    {
                        dirName = dirName.Replace(ResourcesEx.BundleResDir + "/", "");
                    }
                    else
                    {
                        dirName = null;
                    }

                    string newPath = null;
                    if (string.IsNullOrEmpty(dirName))
                    {
                        newPath = fileName;
                    }
                    else
                    {
                        newPath = dirName + "/" + fileName;
                    }


                    if (extName == ".shader")
                    {
                        Shader shader = AssetDatabase.LoadAssetAtPath<Shader>(path);
                        KeyValuePair<string, string> fileBundlePair = new KeyValuePair<string, string>(path, enumerator.Current.Value);
                        if (runtimeAssetToBundleMap.shaderBundleMap.ContainsKey(shader.name))
                        {
                            throw new System.Exception("same key: " + shader.name + "   " + path + "   " + runtimeAssetToBundleMap.shaderBundleMap[shader.name].Key);
                        }
                        runtimeAssetToBundleMap.shaderBundleMap.Add(shader.name, fileBundlePair);
                    }
                    else
                    {
                        if (extName == ".unity")
                        {
                            runtimeAssetToBundleMap.sceneBundleMap.Add(fileName, enumerator.Current.Value);
                        }
                        else
                        {
                            if (dirName == null)
                            {
                                continue;
                            }

                            if (runtimeAssetToBundleMap.assetBundleMap.ContainsKey(newPath))
                            {
                                Debug.LogError("repeat resource: " + newPath);
                            }
                            else
                            {
                                runtimeAssetToBundleMap.assetBundleMap.Add(newPath, new KeyValuePair<string, string>(path, enumerator.Current.Value));
                            }
                        }
                    }
                }

                string assetToBundleMapFileName = NewBundleManager.assetToBundleConfigName;
               
                CommonUtil.ObjectToJsonFile(runtimeAssetToBundleMap, resPath + "/" + assetToBundleMapFileName + ".txt.backup");

                Dictionary<string, string> nameMap = new Dictionary<string, string>();
                bundleLayer = config.bundles;
                foreach (var entry in bundleLayer)
                {
                    string newBundleName = null;
                    newBundleName = entry.Key;
                    if(EditorSwitchConfig.encodeBundleName)
                    {
                        newBundleName = NewBundleManager.ConvertToHashName(entry.Key);
                    }
                    
                    nameMap.Add(entry.Key, newBundleName);
                }

                CommonUtil.ObjectToJsonFile(nameMap, resPath + "/" + "name_map.txt");


                {
                    AssetToBundleMap runtimeAssetToBundleMap2 = new AssetToBundleMap();
                    foreach (var entry in runtimeAssetToBundleMap.assetBundleMap)
                    {
                        KeyValuePair<string, string> pair = entry.Value;
                        pair = new KeyValuePair<string, string>(pair.Key, nameMap[pair.Value]);
                        runtimeAssetToBundleMap2.assetBundleMap.Add(entry.Key, pair);
                    }

                    foreach (var entry in runtimeAssetToBundleMap.shaderBundleMap)
                    {
                        KeyValuePair<string, string> pair = entry.Value;
                        pair = new KeyValuePair<string, string>(pair.Key, nameMap[pair.Value]);
                        runtimeAssetToBundleMap2.shaderBundleMap.Add(entry.Key, pair);
                    }

                    foreach (var entry in runtimeAssetToBundleMap.sceneBundleMap)
                    {
                        runtimeAssetToBundleMap2.sceneBundleMap.Add(entry.Key, nameMap[entry.Value]);
                    }

                    CommonUtil.ObjectToJsonFile(runtimeAssetToBundleMap2, resPath + "/" + assetToBundleMapFileName + ".txt");
                }
            }
        });

        CommonUtil.ObjectToJsonFile(dependenceInfoMap, resPath + "/" + "dependence_info.json");

        AssetDatabase.Refresh();

        //      ExportAssetBundles.CreateDependenceFiles(target);
    }




    static void CheckLoopForBundleConfig(string bundleName, Dictionary<string, Dictionary<string, HashSet<string>>> bundleDependencies, Dictionary<string, string> assetToBundleMap, HashSet<string> logs, HashSet<string> checkedBundleSet = null, List<string> bundleStack = null, List<string> assetPathStack = null)
    {
        if(bundleName != null)
        {
            if(checkedBundleSet.Contains(bundleName))
            {
                if(bundleStack.Count > 1)
                {
                    int findIndex = -1;
                    for(int i = 0; i < bundleStack.Count; i++)
                    {
                        if(bundleStack[i] == bundleName)
                        {
                            findIndex = i;
                        }
                    } 

                    if(findIndex >= 0)
                    {
                        string log = logs.Count + ":";
                        for(int i = findIndex; i < bundleStack.Count; i++)
                        {
                            HashSet<string> parentSet = new HashSet<string>();
                            var t = bundleDependencies[bundleStack[i]];
                            foreach(var entry in t)
                            {
                                if(entry.Value.Contains(assetPathStack[i]))
                                {
                                    parentSet.Add(entry.Key);
                                }
                            }

                            string parentAssetPaths = "";
                            int j = 0;
                            foreach(var p in parentSet)
                            {
                                parentAssetPaths += p;
                                if(j < parentSet.Count - 1)
                                {
                                    parentAssetPaths += " | ";
                                }
                                j++;
                            }
                                
                            log += bundleStack[i] + "(" + parentAssetPaths + " $ " + assetPathStack[i] + ")";
                            if(i < bundleStack.Count - 1)
                            {
                                log += "->";
                            }
                        }
                            
                        logs.Add(log);
                    }
                }
                return;
            }
            checkedBundleSet.Add(bundleName);

            bundleStack.Add(bundleName);

            Dictionary<string, HashSet<string>> temp = bundleDependencies[bundleName];
            foreach(var entry in temp)
            {
                HashSet<string> ds = entry.Value;
                foreach(var d in ds)
                {
                    assetPathStack.Add(d);
                    CheckLoopForBundleConfig(assetToBundleMap[d], bundleDependencies, assetToBundleMap, logs, checkedBundleSet, bundleStack, assetPathStack);
                    assetPathStack.Remove(d);
                }
            }

            bundleStack.Remove(bundleName);
        }
        else
        {
            checkedBundleSet = new HashSet<string>();
            foreach(var entry in bundleDependencies)
            {
                if(checkedBundleSet.Contains(entry.Key))
                {
                    continue;
                }

                bundleStack = new List<string>();
                assetPathStack = new List<string>();
                CheckLoopForBundleConfig(entry.Key, bundleDependencies, assetToBundleMap, logs, checkedBundleSet, bundleStack, assetPathStack);
            }
        }
    }

//    [MenuItem("Assets/Custom/AssetBundleTool/CheckLoop")]
//    static void CheckLoop()
//    {
//        Object obj = Selection.activeObject;
//        string path = AssetDatabase.GetAssetPath(obj);
//        Dictionary<string, string[]> assetToBundleMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, string[]>>(path);
//        CheckLoop(assetToBundleMap);
//    }
//
//    static void CheckLoop(Dictionary<string, string[]> newEditorAssetToBundleMap)
//    {
//        Dictionary<string, Dictionary<string,HashSet<string>>> checkLoopMap = new Dictionary<string, Dictionary<string,HashSet<string>>>();
//        {
//            Dictionary<string, string[]>.Enumerator e = newEditorAssetToBundleMap.GetEnumerator();
//            while (e.MoveNext())
//            {
//                string assetPath = e.Current.Key;
//                if (e.Current.Value.Length <= 1)
//                {
//                    continue;
//                }
//
//                string mainBundleName = e.Current.Value[0];
//                Dictionary<string,HashSet<string>> destMap = null;
//                if (!checkLoopMap.TryGetValue(mainBundleName, out destMap))
//                {
//                    destMap = new Dictionary<string, HashSet<string>>();
//                    checkLoopMap.Add(mainBundleName, destMap);
//                }
//
//                for (int i = 1; i < e.Current.Value.Length; i++)
//                {
//                    string dependedBundleName = e.Current.Value[i];
//
//                    HashSet<string> resPathSet = null;
//                    if (!destMap.TryGetValue(dependedBundleName, out resPathSet))
//                    {
//                        resPathSet = new HashSet<string>();
//                        destMap.Add(dependedBundleName, resPathSet);
//                    }
//
//                    resPathSet.Add(assetPath);
//                }
//            }
//        }
//
//
//        {
//            HashSet<string> checkLoopBundleNameSet = new HashSet<string>();
//            Dictionary<string, Dictionary<string, HashSet<string>>>.Enumerator e = checkLoopMap.GetEnumerator();
//            while (e.MoveNext())
//            {
//                checkLoopBundleNameSet.Add(e.Current.Key);
//                foreach (var b in e.Current.Value.Keys)
//                {
//                    checkLoopBundleNameSet.Add(b);
//                }
//            }
//
//            HashSet<string[]> loopSet = new HashSet<string[]>();
//            LinkedList<string> checkLoopBundleNameQueue = new LinkedList<string>(checkLoopBundleNameSet);
//            while (checkLoopBundleNameQueue.Count > 0)
//            {
//                string firstBundleName = checkLoopBundleNameQueue.First.Value;
//                checkLoopBundleNameQueue.RemoveFirst();
//
//                HashSet<string> travelNodeSet = new HashSet<string>();
//                List<string> s = new List<string>();
//                AddLoopNode(firstBundleName, s, travelNodeSet, checkLoopMap, loopSet);
//
//                foreach (var node in travelNodeSet)
//                {
//                    checkLoopBundleNameQueue.Remove(node);
//                }
//            }
//
//            int maxPrintLooNum = 100;
//            string log = "create bundle config failed, because has loop:" + loopSet.Count + "\n";
//            int loopIndex = 0;
//            foreach (string[] loopArr in loopSet)
//            {
//                if (loopIndex >= maxPrintLooNum)
//                {
//                    break;
//                }
//
//                log += "loop:" + loopIndex + "\n";
//
//                for (int i = 0; i < loopArr.Length; i++)
//                {
//                    string srcBundleName = loopArr[i];
//                    string destBundleName = loopArr[(i + 1) % loopArr.Length];
//
//                    log += "\t" + srcBundleName + "->" + destBundleName + ":\n";
//                    Dictionary<string, HashSet<string>> destMap = null;
//                    if (!checkLoopMap.TryGetValue(srcBundleName, out destMap))
//                    {
//                        throw new System.Exception("src bundle not found: " + srcBundleName);
//                    }
//
//                    HashSet<string> resPaths = null;
//                    if (!destMap.TryGetValue(destBundleName, out resPaths))
//                    {
//                        throw new System.Exception("dest bundle not found: " + destBundleName);
//                    }
//                        
//                    foreach (string resPath in resPaths)
//                    {
//                        log += "\t\t" + resPath + "\n";
//                        break;
//                    }
//                }
//
//                log += "\n";
//                loopIndex++;
//            }
//
//            if (loopSet.Count > 0)
//            {
//                throw new System.Exception(log);
//            }
//            else
//            {
//                Debug.Log("no loop");
//            }
//        }
//    }
//
//    static void AddLoopNode(string bundleName, List<string> stack, HashSet<string> travelSet, Dictionary<string, Dictionary<string, HashSet<string>>> checkLoopMap, HashSet<string[]> loopSet)
//    {
//        int index = stack.IndexOf(bundleName);
//        if (index < 0)
//        {
//            travelSet.Add(bundleName);
//            stack.Add(bundleName);
//
//            Dictionary<string, HashSet<string>> nextNodeMap = null;
//            if (checkLoopMap.TryGetValue(bundleName, out nextNodeMap))
//            {
//                Dictionary<string, HashSet<string>>.Enumerator e = nextNodeMap.GetEnumerator();
//                while (e.MoveNext())
//                {
//                    AddLoopNode(e.Current.Key, stack, travelSet, checkLoopMap, loopSet);
//                }
//            }
//
//            stack.RemoveAt(stack.Count - 1);
//        }
//        else
//        {
//            string[] loopArr = new string[stack.Count - index];
//            for (int i = index; i < stack.Count; i++)
//            {
//                loopArr[i - index] = stack[i];
//            }
//
//            loopSet.Add(loopArr);
//        }
//    }

    static void SearchDepth(string path, Dictionary<string, HashSet<string>> dependenceMap, int depth, Dictionary<string, int> depthMap)
    {
        int tempDepth = 0;
        if (depthMap.TryGetValue(path, out tempDepth))
        {
            if (depth > tempDepth)
            {
                depthMap[path] = depth;
            }
            else
            {
                return;
            }
        }
        else
        {
            depthMap.Add(path, depth);
        }

        HashSet<string> dependedPaths = dependenceMap[path];
        foreach (var p in dependedPaths)
        {
            SearchDepth(p, dependenceMap, depth + 1, depthMap);
        }
    }

    static List<string> SortByDepth(HashSet<string> assetPathSet, Dictionary<string, DependenceInfo> dependenceInfoMap, out Dictionary<string, int> depthMap, out Dictionary<string, HashSet<string>> dependenceMap)
    {
        dependenceMap = new Dictionary<string, HashSet<string>>();
        foreach(string path in assetPathSet)
        {
            HashSet<string> ds = DependenceInfo.GetDependencies(dependenceInfoMap, path, true);
            HashSet<string> dSet = new HashSet<string>(ds);
            HashSet<string> newSet = new HashSet<string>();
            foreach (var a in assetPathSet)
            {
                if (dSet.Contains(a))
                {
                    newSet.Add(a);
                }
            }
            dSet.Remove(path);
            dependenceMap.Add(path, newSet);
        }

        Queue<string> q = new Queue<string>();
        foreach (var entry in dependenceMap)
        {
            bool found = false;
            foreach (var entry2 in dependenceMap)
            {
                if (entry2.Value.Contains(entry.Key))
                {
                    found = true;
                    break;
                }
            }

            if (!found)
            {
                q.Enqueue(entry.Key);
            }
        }

        Dictionary<string, int> tempDepthMap = new Dictionary<string, int>();
        while (q.Count > 0)
        {
            string item = q.Dequeue();
            SearchDepth(item, dependenceMap, 0, tempDepthMap);
        }

        List<string> result = new List<string>(tempDepthMap.Keys);
        result.Sort((a, b) =>
            {
                int ad = 0;
                tempDepthMap.TryGetValue(a, out ad);
                int bd = 0;
                tempDepthMap.TryGetValue(b, out bd);
                int d = ad - bd;
                if(d != 0)
                {
                    return d;
                }

                return string.Compare(a, b);
            });

        depthMap = tempDepthMap;
        return result;
    }

    // [MenuItem("Custom/AssetBundleTool/RenameAssetBundle")]
    // static void RenameAssetBundle()
    // {
    //     string srcPath = "/Users/auto_build/jenkins/workspace/mmorpg_android_alpha/asset_bundle/manifest";
    //     string destPath = "/Users/auto_build/jenkins/workspace/mmorpg_android_alpha/asset_bundle2";
    //     CommonUtil.CreateDir(destPath);

    //     CommonUtil.Copy(srcPath + "/" + BundleVersionManager.configName, destPath + "/" + BundleVersionManager.configName);

    //     BundleVersionConfig tempVersionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(srcPath + "/" + BundleVersionManager.configName);
    //     foreach (var entry in tempVersionConfig.bundles)
    //     {
    //         string srcFilePath = srcPath + "/" + entry.Value.name;
    //         string destFilePath = destPath + "/" + entry.Value.GetHashName();

    //         CommonUtil.Copy(srcFilePath, destFilePath);
    //     }
    // }

    [MenuItem("Assets/Custom/AssetBundleTool/Md5")]
    static void Md5()
    {
        EditorCommonTool.ProcessSelectionObjs("*", (path) =>
            {
                Debug.LogError(path + "  md5:" + MD5Hash.GetMD5HashFromFile(path));
            });
    }

    [MenuItem("Assets/Custom/AssetBundleTool/PrintSameNameFiles")]
    static void PrintSameNameFiles()
    {
        HashSet<string> fileNameSet = new HashSet<string>();
        EditorCommonTool.ProcessSelectionObjs("*", (path) => 
            {
                string fileName = Path.GetFileName(path);
                if(fileNameSet.Contains(fileName))
                {
                    Debug.LogError("repeat fileName: " + fileName);
                }
                else
                {
                    fileNameSet.Add(fileName);
                }
            });
    }

    // [MenuItem("Custom/AssetBundleTool/TestDumpMonoScript")]
    // static void TestDumpMonoScript()
    // {
    //     MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>("Assets/Scripts/CameraCullLayer.cs");
    //     Dictionary<string, List<KeyValuePair<string, string>>> map = MonoScriptDumper.Dump(typeof(TerrainCullingGroup));
    //     CommonUtil.ObjectToJsonFile(map, "test_monoscript.txt");
    // }

    [MenuItem("Custom/AssetBundleTool/TestMoveAsset")]
    static void TestMoveAsset()
    {
        AssetTrash.PutBackAll();
//        HashSet<string> s = new HashSet<string>();
//        s.Add("Assets/build");
//        s.Add("Assets/build_navmesh/build");
//        Trash.Delete(s);
    }


//    static Dictionary<string, System.Diagnostics.Stopwatch> stopWatchMap = new Dictionary<string, System.Diagnostics.Stopwatch>();
//    static void StartStopWatch(string name)
//    {
//        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
//        sw.Start();
//
//        stopWatchMap.Add(name, sw);
//    }
//
//    static float StopStopWatch(string name)
//    {
//        System.Diagnostics.Stopwatch sw = stopWatchMap[name];
//        stopWatchMap.Remove(name);
//
//        sw.Stop();
//        return sw.ElapsedMilliseconds / 1000f;
//    }

//     [MenuItem("Custom/AssetBundleTool/TestMonoScript")]
//     static void TestMonoScript()
//     {
//         Dictionary<string, List<KeyValuePair<string, string>>> monoScriptInfoMap = new Dictionary<string, List<KeyValuePair<string, string>>>();

// //        foreach (var scriptPath in scriptFilePathSet)
// //        {
// //            MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(scriptPath);
// //            MonoScriptDumper.DumpType(script.GetClass(), monoScriptInfoMap);
// //        }

//         ToLuaMenu.BindType[] luaTypes = CustomSettings.customTypeList;
//         for (int i = 0; i < luaTypes.Length; i++)
//         {
//             if(i == luaTypes.Length - 1)
//             {
//                 Debug.LogError("test");
//             }

//             ToLuaMenu.BindType type = luaTypes[i];
//             MonoScriptDumper.DumpType(type.type, true, monoScriptInfoMap);
//         }

//         CommonUtil.ObjectToJsonFile(monoScriptInfoMap, "monoscript_info.txt");
//     }

    [MenuItem("Custom/AssetBundleTool/testGuid")]
    static void testGuid()
    {
        Debug.LogError(AssetDatabase.GUIDToAssetPath("88ff7dd459be2164bb481bf2019a26d9"));
    }

    // [MenuItem("Custom/AssetBundleTool/CompareDependenceInfo")]
    // static void CompareDependenceInfo()
    // {
    //     Dictionary<string, DependenceInfo> dependenceInfoMap2 = CommonUtil.ObjectFromJsonFile<Dictionary<string, DependenceInfo>>("/Users/auto_build/Desktop/asset_bundle/dependence_info.json");
    //     Dictionary<string, DependenceInfo> dependenceInfoMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, DependenceInfo>>("/Users/auto_build/jenkins/workspace/mmorpg_android_alpha/project/Assets/asset_bundle/dependence_info.json");

    //     Dictionary<string, HashSet<string>> set = new Dictionary<string, HashSet<string>>();
    //     foreach (var entry in dependenceInfoMap)
    //     {
    //         DependenceInfo dset = null;
    //         if (dependenceInfoMap2.TryGetValue(entry.Key, out dset))
    //         {
    //             HashSet<string> diffset = new HashSet<string>();
    //             foreach (string d in dset.directDependencies)
    //             {
    //                 if (!entry.Value.directDependencies.Contains(d))
    //                 {
    //                     diffset.Add(d);
    //                 }
    //             }

    //             foreach (string d in entry.Value.directDependencies)
    //             {
    //                 if (!dset.directDependencies.Contains(d))
    //                 {
    //                     diffset.Add(d);
    //                 }
    //             }

    //             if (diffset.Count > 0)
    //             {
    //                 set.Add(entry.Key, diffset);
    //             }
    //         }
    //         else
    //         {
    //             set.Add(entry.Key, entry.Value.directDependencies);
    //         }
    //     }

    //     foreach (var entry in dependenceInfoMap2)
    //     {
    //         DependenceInfo dset = null;
    //         if (!dependenceInfoMap.TryGetValue(entry.Key, out dset))
    //         {
    //             set.Add(entry.Key, entry.Value.directDependencies);
    //         }
    //     }

    //     CommonUtil.ObjectToJsonFile(set, "test_de.txt");
    // }

    [MenuItem("Custom/AssetBundleTool/MergeToSuitableAssetBundleVersion")]
    public static void MergeToSuitableAssetBundleVersion()
    {
        int version = 0;
        string mergeDir = null;
        string oldConfigPath = null;
        string svnUserName = null;
        string svnPassword = null;
        string[] args = System.Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; i++)
        {
            if (args[i] == "-version")
            {
                version = int.Parse(args[++i]);
            }
            else if (args[i] == "-merge_dir")
            {
                mergeDir = args[++i];
            }
            else if (args[i] == "-old_config_path")
            {
                oldConfigPath = args[++i];
            }
            else if (args[i] == "-svn_username")
            {
                svnUserName = args[++i];
            }
            else if (args[i] == "-svn_password")
            {
                svnPassword = args[++i];
            }
        }

        if (string.IsNullOrEmpty(mergeDir) || string.IsNullOrEmpty(oldConfigPath))
        {
            throw new System.Exception("path is null: mergeDir: " + mergeDir + "   oldConfigPath:" + oldConfigPath);
        }

        if(!System.IO.Directory.Exists(oldConfigPath))
        {
            return;
        }

        BundleVersionConfig versionConfig = null;
        string versionConfigPath = null;
        string[] configPaths = System.IO.Directory.GetFiles(oldConfigPath, "config_*", SearchOption.TopDirectoryOnly);
        for (int i = 0; i < configPaths.Length; i++)
        {
            string configPath = configPaths[i];

            BundleVersionConfig tempConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(configPath);
            if (tempConfig == null)
            {
                continue;
            }

            if (version <= 0 || version >= tempConfig.min_app_version)
            {
                if (versionConfig == null)
                {
                    versionConfig = tempConfig;
                    versionConfigPath = configPath;
                }
                else
                {
                    if (tempConfig.version > versionConfig.version)
                    {
                        versionConfig = tempConfig;
                        versionConfigPath = configPath;
                    }
                    else if (tempConfig.version == versionConfig.version)
                    {
                        System.DateTime tempTime = System.Convert.ToDateTime(tempConfig.build_time);
                        System.DateTime configBuildTime = System.Convert.ToDateTime(versionConfig.build_time);
                        if (tempTime > configBuildTime)
                        {
                            versionConfig = tempConfig;
                            versionConfigPath = configPath;
                        }
                    }
                }
            }
        }

        if (versionConfigPath != null)
        {
            if(System.IO.Directory.Exists(mergeDir + "/.svn"))
            {
                int lastChangedVersion = SvnTool.GetLastChangedVersion(versionConfigPath);
                if (lastChangedVersion <= 0)
                {
                    throw new System.Exception("get last chnaged version failed: " + lastChangedVersion);
                }
                    
                string svnOtherParams = "--non-interactive --trust-server-cert-failures=unknown-ca,cn-mismatch,not-yet-valid,other";
                if (!string.IsNullOrEmpty(svnUserName) && !string.IsNullOrEmpty(svnPassword))
                {
                    svnOtherParams += " --username " + svnUserName + " --password " + svnPassword;
                }
                string err = SvnTool.MergeToVersion(mergeDir, lastChangedVersion, svnOtherParams);
                if (err != null)
                {
                    throw new System.Exception(err);
                }
            }
            if(System.IO.Directory.Exists(mergeDir + "/.git"))
            {
                string lastChangedVersion = GitTool.GetLastChangedVersion(versionConfigPath);
                if (string.IsNullOrEmpty(lastChangedVersion))
                {
                    throw new System.Exception("get last chnaged version failed: " + lastChangedVersion);
                }
                    
                string svnOtherParams = "--non-interactive --trust-server-cert-failures=unknown-ca,cn-mismatch,not-yet-valid,other";
                if (!string.IsNullOrEmpty(svnUserName) && !string.IsNullOrEmpty(svnPassword))
                {
                    svnOtherParams += " --username " + svnUserName + " --password " + svnPassword;
                }
                string err = GitTool.MergeToVersion(mergeDir, lastChangedVersion, svnOtherParams);
                if (err != null)
                {
                    throw new System.Exception(err);
                }
            }
        }
        else
        {
            CommonUtil.Delete(mergeDir);
            CommonUtil.CreateDir(mergeDir);
        }
    }

    [MenuItem("Custom/AssetBundleTool/CompareAllBundle")]
    static void CompareAllBundle()
    {

        string dir1 = "/Users/username/Downloads/test_bundle_bug/bundle2";
        string dir0 = "/Users/username/Downloads/test_bundle_bug/bundle";
        string[] files = System.IO.Directory.GetFiles(dir0, "*", System.IO.SearchOption.TopDirectoryOnly);
        for (int i = 0; i < files.Length; i++)
        {
            string log = null;
            string filePath = files[i];
            string fileName = System.IO.Path.GetFileName(filePath);
            if (fileName.StartsWith("."))
            {
                continue;
            }

            string filePath1 = System.IO.Path.Combine(dir1, fileName);
            if (System.IO.File.Exists(filePath1))
            {
                byte[] data0 = CommonUtil.fileToBytes(filePath);
                byte[] data1 = CommonUtil.fileToBytes(filePath1);

                int beginIndex = -1;
                for(int j = 0; j < data0.Length; j++)
                {
                    if (data1[j] != data0[j])
                    {
                        if (beginIndex < 0)
                        {
                            beginIndex = j;
                        }
                    }
                    else
                    {
                        if (beginIndex >= 0)
                        {
                            if (log == null)
                            {
                                log += fileName + ": ";
                            }

                            log += "(" + beginIndex + "," + (j - 1) +"), ";
                            beginIndex = -1;
                        }
                    }
                }

                if (beginIndex >= 0)
                {
                    if (log == null)
                    {
                        log += fileName + ": ";
                    }

                    log += "(" + beginIndex + "," + (data0.Length - 1) +"), ";
                    beginIndex = -1;
                }
            }
            else
            {
                Debug.LogError("file not exist: " + filePath1);
            }

            if (!string.IsNullOrEmpty(log))
            {
                Debug.LogError(log);
            }
        }
    }

    // [MenuItem("Custom/AssetBundleTool/CountBundleSize")]
    // static void CountBundleSize()
    // {
    //     string dir = "/Users/username/my_data/work/mmo/asset_bundle/android/alpha/manifest";
    //     string backupDir = dir + "/asset_bundle";

    //     Dictionary<string, string> nameMap = CommonUtil.ObjectFromJsonFile<Dictionary<string, string>>(backupDir + "/name_map.txt");

    //     BundleVersionConfig versionConfig = CommonUtil.ObjectFromJsonFile<BundleVersionConfig>(dir + "/" + BundleVersionManager.configName);

    //     BundleConfig rawConfig = CommonUtil.ObjectFromJsonFile<BundleConfig>(backupDir + "/raw_config.json");

    //     AssetBundle a = AssetBundle.LoadFromFile(dir + "/manifest");
    //     AssetBundleManifest manifest = a.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
    //     a.Unload(false);

    //     Dictionary<string, HashSet<string>> bundleNameMap = new Dictionary<string, HashSet<string>>();


    //     foreach (var entry2 in nameMap)
    //     {
    //         string realBundleName = null;
    //         if (entry2.Key.StartsWith("c__"))
    //         {
    //             realBundleName = "common";
    //         }
    //         else if (entry2.Key.Contains("_rgb_") || entry2.Key.EndsWith("_rgb") || entry2.Key.Contains("_rgba_") || entry2.Key.EndsWith("_rgba"))
    //         {
    //             realBundleName = "sprite_sheet";
    //         }
    //         else
    //         {
    //             if (entry2.Key.ToLower().Contains("_runtime"))
    //             {
    //                 int index = entry2.Key.IndexOf("_runtime");
    //                 realBundleName = entry2.Key.Substring(0, index + "_runtime".Length);
    //             }

    //             if (realBundleName == null)
    //             {
    //                 foreach (var entry in rawConfig.bundles)
    //                 {
    //                     string bundleName = entry.Key;
    //                     if (entry2.Key.StartsWith(bundleName))
    //                     {
    //                         realBundleName = bundleName;
    //                         break;
    //                     }
    //                 }
    //             }
    //         }

    //         if (string.IsNullOrEmpty(realBundleName))
    //         {
    //             realBundleName = entry2.Key;
    //         }

    //         HashSet<string> s = null;
    //         if (!bundleNameMap.TryGetValue(realBundleName, out s))
    //         {
    //             s = new HashSet<string>();
    //             bundleNameMap.Add(realBundleName, s);
    //         }

    //         s.Add(entry2.Value);
    //     }
            
    //     uint totalSize = 0;
    //     string sizeCountLog = "";
    //     foreach (var entry in bundleNameMap)
    //     {
    //         uint totalBundleSize = 0;

    //         HashSet<string> dependSet = new HashSet<string>();
    //         foreach (var bundleName in entry.Value)
    //         {
    //             if (!versionConfig.bundles.ContainsKey(bundleName))
    //             {
    //                 Debug.LogError("not found bundle: " + bundleName);
    //                 continue;
    //             }

    //             BundleInfo bundleInfo = versionConfig.bundles[bundleName];
    //             totalBundleSize += bundleInfo.size;
    //             totalSize += bundleInfo.size;

    //             string[] depends = manifest.GetAllDependencies(bundleName);
    //             for (int i = 0; i < depends.Length; i++)
    //             {
    //                 string depend = depends[i];
    //                 if (!entry.Value.Contains(depend))
    //                 {
    //                     dependSet.Add(depend);
    //                 }
    //             }
    //         }

    //         uint dependSize = 0;
    //         foreach (var d in dependSet)
    //         {
    //             if (!versionConfig.bundles.ContainsKey(d))
    //             {
    //                 Debug.LogError("not found bundle: " + d);
    //                 continue;
    //             }

    //             BundleInfo bundleInfo = versionConfig.bundles[d];
    //             dependSize += bundleInfo.size;
    //         }

    //         sizeCountLog += entry.Key + ": " + CommonUtil.toSizeString(totalBundleSize) + "   " + CommonUtil.toSizeString(dependSize) + "   " + CommonUtil.toSizeString(totalBundleSize + dependSize) + "\n";
    //     }

    //     sizeCountLog = "total" + ": " + CommonUtil.toSizeString(totalSize) + "\n" + sizeCountLog;

    //     Debug.LogError("size log: \n" + sizeCountLog);
    // }

    [MenuItem("Custom/AssetBundleTool/PrintMostDependenceAssetBundles")]
    static void PrintMostDependenceAssetBundles()
    {
        AssetBundle ab = AssetBundle.LoadFromFile("manifest");
        AssetBundleManifest manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        string[] bundeNames = manifest.GetAllAssetBundles();
        Dictionary<string, int> bundleNameDepenedenceCountMap = new Dictionary<string, int>();
        for(int i = 0; i < bundeNames.Length; i++)
        {
            string bundleName = bundeNames[i];
            string[] a = manifest.GetAllDependencies(bundleName);
            bundleNameDepenedenceCountMap.Add(bundleName, a.Length);
        }

        SortByDependenceCountComparer c = new SortByDependenceCountComparer();
        c.dcm = bundleNameDepenedenceCountMap;
        System.Array.Sort(bundeNames, c);

        string log = "";
        for(int i = 0; i < bundeNames.Length && i < 50; i++)
        {
            string bundleName = bundeNames[i];
            log += bundleName + ": " + bundleNameDepenedenceCountMap[bundleName] + "\n";
        }

        ab.Unload(true);

        Debug.LogError(log);

//        Dictionary<string, string> d = CommonUtil.ObjectFromJsonFile<Dictionary<string, string>>("name_map.txt");
//        Dictionary<string, string> newD = new Dictionary<string, string>();
//        foreach(var entry in d)
//        {
//            newD.Add(entry.Value, entry.Key);
//        }
//
//        string log = "";
//        string t = CommonUtil.fileToString("test_asset_bundle.txt");
//        string[] md5s = t.Split('\n');
//        for(int i = 0; i < md5s.Length; i++)
//        {
//            string md5 = md5s[i];
//            string realName = newD[md5];
//            log += realName + ": " + md5 + "\n";
//        }
//
//        CommonUtil.stringToFile("find_real_name.txt", log);
    }
}